home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / web / fweb / fweb-1.40 / web / ftangle.web < prev    next >
Text File  |  1993-10-29  |  187KB  |  7,953 lines

  1. @z --- ftangle.web ---
  2.  
  3. FWEB version 1.40 (October 30, 1993)
  4.  
  5. Based on version 0.5 of S. Levy's CWEB [copyright (C) 1987 Princeton University]
  6.  
  7. @x-----------------------------------------------------------------------------
  8.  
  9.  
  10. \Title{FTANGLE.WEB} % The FTANGLE processor.
  11.  
  12. @c
  13.  
  14. @* INTRODUCTION.  \FTANGLE\ has a fairly straightforward outline.  It
  15. operates in two phases: first it reads the source file, saving the code in
  16. compressed form; then outputs the code, after shuffling it around.  It can
  17. be compiled with the optional flag |DEBUG|. (See \.{typedefs.hweb}.)
  18.  
  19. @m _FTANGLE_ // Identifies this module to the \.{*.hweb} header files.
  20. @d _FTANGLE_h 
  21. @d _FWEB_h
  22.  
  23. @A
  24. @<Possibly split into parts@>@;
  25.  
  26. @<Include files@>@;
  27. @<Typedef declarations@>@;
  28. @<Prototypes@>@;
  29. @<Global variables@>@;
  30.  
  31. /* For pc's, the file is split into three compilable parts using the
  32. compiler-line macro |part|, which must equal either~1, 2, or~3. */
  33. #if(part == 0 || part == 1)
  34.     @<Part 1@>@;
  35. #endif // |Part == 1|
  36.  
  37. #if(part == 0 || part == 2)
  38.     @<Part 2@>@;
  39. #endif // |part == 2|
  40.  
  41. #if(part == 0 || part == 3)
  42.     @<Part 3@>@;
  43. #endif // |part == 3|
  44.  
  45. @ Here is the main program.  See the user's manual for a detailed
  46. description of the command line.
  47. @<Part 1@>=@[
  48.  
  49. int main FCN((ac, av))
  50.     int ac C0("Number of arguments.")@;
  51.     outer_char **av C1("Argument list.")@;
  52. {
  53. ini_timer(); /* Timing statistics are printed at the end of the run; see
  54.             \.{common.web}. */
  55.  
  56. /* Remember the arguments to |main| in global variables. */
  57.   argc = ac; @+ argv = av;
  58.  
  59.   ini_program(tangle); // Set the |program| flag etc.; see \.{common.web}.
  60.  
  61. @<Initialize everything@>;
  62.  
  63.   phase1(); // Read all the user's text and compress it into |tok_mem|.
  64.   phase2(); // Output the contents of the compressed tables.
  65.  
  66.   if(statistics) see_tstatistics(); // Optional statistical info.
  67.  
  68. return wrap_up(); // We actually |exit| from here.
  69. }
  70.  
  71. @ Here are the initializations done before the beginning of phase~1.
  72. @<Initialize everything@>=
  73. {
  74. @<Allocate initial tables@>@; // Stuff that must be used for command line.
  75. common_init(); // Expand the command line here.
  76. @<Allocate dynamic memory@>@; // Local dynamic memory.
  77. @<Set initial values@>@;
  78. ini_internal_fcns(); // Internal built-in function macros.
  79. ini_Ratfor(); // Initialize \Ratfor.
  80. }
  81.  
  82. @I typedefs.hweb // Declarations common to both \FTANGLE\ and \FWEAVE.
  83.  
  84. @I val.hweb // Stuff for expression evaluation.
  85.  
  86. @I macs.hweb // Macros for macro processing.
  87.  
  88.  
  89. @ The function prototypes must appear before the global variables.
  90. @<Proto...@>=
  91.  
  92. #include "t_type.h" // Function prototypes for \FTANGLE.
  93.  
  94. @* CODE for OUTPUT. When |language != C|, we must remember the last fragment
  95. of code in order to implement |+=| and similar operators for \Fortran\ or
  96. \Ratfor. 
  97.  
  98. @d RST_LAST_EXPR {plast_char = last_char; last_xpr_overflowed = NO;}
  99.  
  100. @d INDENT_SIZE 2 /* Number of columns to indent for each level of
  101. beautified Ratfor output. (Put into style file?) */
  102.  
  103. @<Glob...@>=
  104.  
  105. EXTERN int indnt_size SET(INDENT_SIZE); 
  106.     // So we can interface to \.{rat77.web}.
  107.  
  108. EXTERN outer_char HUGE *last_char, HUGE *last_end; // Dynamic array.
  109. EXTERN outer_char HUGE *plast_char; // Current position in |last_char|.
  110. EXTERN BUF_SIZE max_expr_chars; // Allocated length of |last_char|.
  111.  
  112. EXTERN boolean last_xpr_overflowed SET(NO);
  113.  
  114. EXTERN int indent_level SET(0); // Current state of Ratfor output.
  115.  
  116. @ Allocate the |last_char| array.
  117. @<Allocate dynamic memory@>=
  118.  
  119. ALLOC(outer_char,last_char,ABBREV(max_expr_chars),max_expr_chars,0);
  120. last_end = last_char + max_expr_chars;
  121. plast_char = last_char;
  122.  
  123. @ An interface to \.{rat77.web}
  124. @<Part 1@>=@[
  125.  
  126. SRTN rst_last(VOID)@/
  127. RST_LAST_EXPR@;
  128.  
  129. @ For speed, we'll buffer up the C~output.  Characters are put temporarily
  130. into |C_buffer|.  That buffer is flushed whenever a newline is emitted.  If
  131. the buffer ever gets full, an attempt is made to split the buffer at a
  132. reasonable place.  To accomplish that, we have the array |str_start| of
  133. pointers to positions in |C_buffer|.  The odd elements in |str_start| are
  134. the positions at which |stringg| mode starts; the even elements are the
  135. positions after |stringg| mode ends.  The zeroth element is |C_buffer|
  136. itself, and the last position, which should always be odd, is |pC_buffer|,
  137. the current position in the buffer.  Thus, if there are no strings in the
  138. buffer, we have |str_start[0] == C_buffer|, |str_start[1] == pC_buffer|.
  139. The ranges allowed to be split are $[0,1)$, $[2,3)$, etc.
  140.  
  141. @<Glob...@>=
  142.  
  143. /* --- Output buffer for C --- */
  144. EXTERN outer_char HUGE *C_buffer, HUGE *pC_end; // Dynamically allocated.
  145. EXTERN outer_char HUGE *pC_buffer; // Current position.
  146. EXTERN BUF_SIZE C_buf_size; // Length of dynamic buffer array.
  147.  
  148. /* --- String positions in that buffer --- */
  149. EXTERN outer_char HUGE *split_pos; // Current position.
  150.  
  151. /* --- Output buffer for \TeX\ --- */
  152. EXTERN outer_char HUGE *X_buffer, HUGE *pX_end; // Dynamically allocated.
  153. EXTERN outer_char HUGE *pX_buffer; // Current position.
  154. EXTERN BUF_SIZE X_buf_size; // Length of dynamic buffer array.
  155.  
  156. @
  157. @<Allocate dyn...@>=
  158.  
  159. /* --- Allocate C output buffer --- */
  160. ALLOC(outer_char,C_buffer,ABBREV(C_buf_size),C_buf_size,0);
  161. pC_end = C_buffer + C_buf_size - 1; // Allow for extra backslash if necessary. 
  162. pC_buffer = C_buffer; // Initialize to beginning.
  163.  
  164. #if FANCY_SPLIT
  165.     @<Reset split position@>@;
  166. #endif /* |FANCY_SPLIT| */
  167.  
  168. /* --- Allocate \TeX\ output buffer --- */
  169. ALLOC(outer_char,X_buffer,ABBREV(X_buf_size),X_buf_size,0);
  170. pX_end = X_buffer + X_buf_size;
  171. pX_buffer = X_buffer; // Initialize to beginning.
  172.  
  173. @ The |split_C| routine is called whenever |C_buffer| fills.  If the flag
  174. |FANCY_SPLIT| is off (the ANSI case), we just continue everything with a
  175. backslash.  Otherwise, we do a fancy break, described below.
  176. @<Part 1@>=@[
  177.  
  178. SRTN split_C(VOID)
  179. {
  180. #if FANCY_SPLIT
  181.     @<Fancy split@>@;
  182. #else
  183.     @<Emit a backslash and newline@>@;
  184. #endif /* |FANCY_SPLIT| */
  185. }
  186.  
  187. @ For the fancy split, we do the following: If we are in a string at this
  188. moment, we emit a backslash and dump the whole buffer.  Otherwise, we hunt
  189. through the |str_start| array for allowable positions to break.  
  190.  
  191. @<Fancy split@>=
  192. @{
  193. /* Split strings, but not constants. */
  194. if(in_string && split_pos == C_buffer)
  195.     {
  196.     @<Emit a backslash and newline@>@;
  197.     return;
  198.     }
  199.  
  200. *pC_buffer = '\0';
  201. split0_C(split_pos);
  202. }
  203.  
  204. @ When we buffer stuff out in any way, e.g. with |C_out|, we must reset the
  205. split position.
  206.  
  207. @<Reset split position@>=
  208. {
  209. split_pos = C_buffer;
  210. }
  211.  
  212. @ Here is the bare-bones C~continuation.
  213.  
  214. @d NO_INDENT 0
  215. @d INDENT 2
  216.  
  217. @<Emit a backsl...@>=
  218. {
  219. if(!meta_mode)
  220.     *pC_buffer++ = '\\'; // There's always room for one more character.
  221.  
  222. C_out(C_buffer,pC_buffer,&pC_buffer,OC("\n"),'\0',NO_INDENT); 
  223.     // Dump out all the way to the end.
  224. }
  225.  
  226. @ Here, given an allowable range we search for a split position.
  227. @<Part 1@>=
  228.  
  229. #if FANCY_SPLIT
  230. @[
  231. SRTN split0_C FCN((p))
  232.     outer_char *p C1("Position for the split")@;
  233. {
  234. int indent;
  235.  
  236. /* If no break has been found, force a break at the end. */
  237. if(p==C_buffer) 
  238.     {
  239.     *pC_buffer++ = '\\';
  240.     p = pC_buffer;
  241.     indent = NO_INDENT;
  242.     }
  243. else indent = INDENT;
  244.  
  245. C_out(C_buffer,p,&pC_buffer,OC("\n"),'\0',indent);
  246.     // Output from |C_buffer| to~|p|.
  247.  
  248. @#if 0
  249.     UPDATE_TERMINAL;
  250.     printf("\nOutput line %u split\n",OUTPUT_LINE);
  251. @#endif
  252. }
  253. #endif // |FANCY_SPLIT|
  254.  
  255. @ Write out (part of) the |C_buffer|.
  256. @<Part 1@>=@[
  257.  
  258. SRTN C_out FCN((C_buffer,p,ppC_buffer,end_str,begin_char,indent))
  259.     outer_char HUGE *C_buffer C0("Buffer we're working with")@;
  260.     outer_char HUGE *p C0("End (next available pos)")@;
  261.     outer_char HUGE * HUGE *ppC_buffer C0("")@;
  262.     outer_char *end_str C0("")@;
  263.     outer_char begin_char C0("")@;
  264.     int indent C1("Should the next buffer be indented?")@;
  265. {
  266. int n = *ppC_buffer - p; /* How many left in buffer. */
  267.  
  268. if(p > C_buffer)
  269.     WRITE1(C_buffer,p-C_buffer)@;
  270.  
  271. /* Add trailing characters if necessary. */
  272. if(*end_str) WRITE1(end_str,STRLEN(end_str))@;
  273.  
  274. /* Reset the pointer, then insert the beginning character if necessary. */
  275. *ppC_buffer = C_buffer;
  276. if(begin_char) *(*ppC_buffer)++ = begin_char;
  277.  
  278. /* Add optional indentation (i.e., fill with blanks). */ 
  279. while(indent--)
  280.     *(*ppC_buffer)++ = ' ';
  281.  
  282. /* If there's still stuff in the buffer, move it to the beginning. */
  283. if(n)
  284.     {
  285.     STRNCPY(*ppC_buffer,p,n);
  286.     *ppC_buffer += n; // Reset the current pointer if necessary.
  287.     }
  288.  
  289. flush0(); // Count the lines.
  290. @<Reset split...@>@;
  291. }
  292.  
  293. @ Here are output macros and routines for \FTANGLE. 
  294.  
  295. @d OUT_FILE outp_file[lan_num(out_language)] // Output of \FTANGLE.
  296.  
  297. /* The next may not be used. */
  298. @d C_printf(c,a) 
  299.     {
  300.     if(!out_file) open_out(OC(""),YES);
  301.     if(fprintf(out_file,c,a)<0) out_error(OC("fprintf"));
  302.     }
  303.  
  304. @<Part 1@>=@[
  305.  
  306. SRTN C_putc FCN((c))
  307.     outer_char c C1("Character to be sent to output.")@;
  308. {
  309. CHECK_OPEN; // Output files are opened only when necessary.
  310.  
  311. /* Remember the output character, since we may want to spit it out again
  312. later, as in |i *= expr| $\to$ |i = i*(expr)|. Turning off the
  313. |compound_assignments| flag by option \.{-+} will speed things up a bit. */
  314. if(compound_assignments && FORTRAN_LIKE(language))
  315.     if(plast_char >= last_end) last_xpr_overflowed = YES;
  316.     else *plast_char++ = c;
  317.  
  318. if(dbg_output) printf("c = '%c' (0x%x)\n",c,c);
  319.  
  320. if(at_beginning && meta_mode && !nuweb_mode && in_string)
  321.     { /* Invoke |C_putc| recursively. */
  322.     at_beginning = NO; // Prevent infinite recursion.
  323.     out_pos = 0; // For \Fortran.
  324.     pmeta = &t_style.meta[lan_num(language)];
  325.     OUT_STR(in_version ? pmeta->hdr.prefx : pmeta->msg.prefx);
  326.     }
  327.  
  328. switch(language)
  329.     {
  330.  case RATFOR: 
  331.  case RATFOR_90:
  332.     if(!Ratfor77) 
  333.         {
  334.         RAT_out(c); /* Old-style \Ratfor. Modern
  335. \Ratfor\ falls through to \Fortran. */
  336.         break;
  337.         }
  338.  
  339.  case FORTRAN: 
  340.  case FORTRAN_90:
  341.     if(reverse_indices && !in_string)
  342.         @<Reverse \Fortran\ indices@>@;
  343.     else
  344.         buffer_out(c);
  345.  
  346.     break;
  347.  
  348.  case LITERAL:
  349.  case TEX:
  350.     @<Buffer \TeX\ output@>@;
  351.     break;
  352.  
  353.  case C: 
  354.  case C_PLUS_PLUS:
  355.  default:
  356. #ifndef mac // \.{Machine-dependent}: Don't buffer C output.
  357.     @<Buffer C output@>@;
  358. /* If the above buffering (a relatively recent addition) doesn't work, use
  359. the following: */ 
  360. #else
  361.     if(c == '\n') flush0(); // Count the lines.
  362.     PUTC(c); 
  363. #endif /* |mac| */
  364.     break;
  365.     }
  366.  
  367. at_beginning = BOOLEAN(c=='\n');
  368. }
  369.  
  370. @ A recent addition to speed up the C~output.
  371. @<Buffer C output@>=
  372. @{
  373. *pC_buffer++ = c; // Add present character to buffer.
  374.  
  375. if(c == '\n') 
  376.     C_out(C_buffer,pC_buffer,&pC_buffer,OC(""),'\0',NO_INDENT); 
  377.     // Output whole buffer.
  378. else if(pC_buffer == pC_end) 
  379.     split_C();
  380. }
  381.  
  382. @
  383. @<Buffer \TeX...@>=
  384. {
  385. *pX_buffer++ = c; /* Add present character to buffer. */
  386.  
  387. if(c == '\n') 
  388.     C_out(X_buffer,pX_buffer,&pX_buffer,OC(""),
  389.      (outer_char)CHOICE(meta_mode && language==TEX, '%', '\0'),NO_INDENT);
  390. else if(pX_buffer == pX_end) 
  391.     split_X();
  392. }
  393.  
  394. @
  395. @<Part 1@>=@[
  396.  
  397. SRTN split_X(VOID)
  398. {
  399. outer_char HUGE *p = pX_buffer;
  400.  
  401. WHILE()
  402.     {
  403.     if(p==X_buffer) @<Print warning message about unsplittable \TeX\
  404. line, break the line, and |return|@>@;
  405.  
  406.     if(*p == ' ')
  407.         {
  408.         C_out(X_buffer,p+1,&pX_buffer,OC("\n"),
  409.          (outer_char)CHOICE(meta_mode && language==TEX, '%', '\0'),
  410.          NO_INDENT);
  411.         return;
  412.         }
  413.  
  414.     if(*(p--) == '\\' && *p != '\\')
  415.         {
  416.         C_out(X_buffer,p+1,&pX_buffer,
  417.          language==TEX ? OC("%\n") : OC("\n"),
  418.          (outer_char)CHOICE(meta_mode && language==TEX, '%', '\0'),
  419.          NO_INDENT);
  420.         return;
  421.         }
  422.     }
  423. }
  424.  
  425. @
  426. @<Print warning message about unsplit...@>=
  427. {
  428. ERR_PRINT(T,"Line had to be broken");
  429. C_out(X_buffer,pX_buffer,&pX_buffer,
  430.     language==TEX ? OC("%\n") : OC("\n"),
  431.     '\0',NO_INDENT);
  432. return;
  433. }
  434.  
  435. @ For~C, output of characters is very simple: use of |putc| suffices. For
  436. \Ratfor\ it's just slightly more complicated; we have to intercept the
  437. meta-comment characters. (We don't need to use metacomments in~C, since
  438. C~has its own preprocessor.) \Fortran\ is much more involved; we must
  439. buffer things up, then flush them out line by line in order to respect the
  440. 72~column restriction and emit continuation characters appropriately.
  441.  
  442. @d NOT_CONTINUATION 0
  443. @d CONTINUATION 1
  444.  
  445. @<Part 1@>=@[
  446.  
  447. static outer_char last_out = '\0'; // In \Fortran, the last character output.
  448.  
  449. /* Various flags help \Fortran\ out. */
  450. static boolean is_label = NO;
  451. static boolean should_continue = NO;
  452. static continuation_line = NOT_CONTINUATION;
  453.  
  454. static STMT_LBL stmt_num[50]; /* Archaic; for numbering
  455.             |do|s in \Fortran. Should use \Ratfor\ instead. */
  456. static short do_level = 0;
  457.  
  458. @ The following variables are needed in both parts~1 and~2.
  459. @<Glob...@>=
  460.  
  461. EXTERN int rst_pos SET(0); // The position immediately after resetting.
  462. EXTERN int out_pos SET(0); // Current position in \Fortran's output buffer.
  463. EXTERN boolean in_string SET(NO); // Faster version of the output state.
  464. EXTERN boolean in_constant SET(NO); // Ditto.
  465. EXTERN boolean started_vcmnt SET(NO);
  466. EXTERN boolean meta_mode SET(NO);
  467.  
  468. @ The function |C_putc| is used for all languages in order to send the
  469. character to the right place. Here is a routine which formats and prints
  470. out a string. It's used in printing out the line information.  Note use of
  471. the macro |vsprintf_| to take account of the different way that Sun-CC
  472. handles variable arguments.
  473.  
  474. @d N_STRBUF 150
  475.  
  476. @<Part 1@>=@[
  477.  
  478. SRTN C_sprintf FCN(VA_ALIST((fmt,n VA_ARGS)))
  479.     VA_DCL(
  480.     CONST outer_char fmt[] C0("String to be printed.")@;
  481.     int n C2("Number of arguments to follow."))@;
  482. {
  483. VA_LIST(arg_ptr)@;
  484. outer_char temp[N_STRBUF];
  485. outer_char HUGE *t;
  486.  
  487. VA_START(arg_ptr,n);
  488. vsprintf_((char *)temp,(CONST char *)fmt,arg_ptr)@;  // Length not checked now.
  489. va_end(arg_ptr);
  490.  
  491. for(t=temp; *t; ++t) C_putc(*t);
  492. }
  493.  
  494. @ Here is \Ratfor's output routine. All it does is intercept the
  495. meta-comment characters and makes the intervening text into a comment.
  496.  
  497. @d send_new_line RST_LAST_EXPR@; flush0(); PUTC('\n')
  498.  
  499. @<Part 1@>=@[
  500.  
  501. SRTN RAT_out FCN((c))
  502.     outer_char c C1("Output this character to \Ratfor.")@;
  503. {
  504. switch(c)
  505.     {
  506.     case end_meta:
  507.         send_new_line;
  508.         return;
  509.  
  510.     case begin_meta:
  511.         if(meta_mode) return; // The second in a row.
  512.         meta_mode = YES; // NOTE: FALLS THROUGH to next case.
  513.  
  514.     case '\n':
  515.         send_new_line;
  516.         if(meta_mode) PUTC('#'); // \Ratfor\ comment.
  517.         return;
  518.  
  519.     default:
  520.         PUTC(c);
  521.         return;
  522.     }
  523. }
  524.         
  525. @ \Fortran's output routine is much more complicated, because things have
  526. to be buffered up.
  527.  
  528. @<Glob...@>=
  529.  
  530. IN_COMMON outer_char outp_buf[];    // \Fortran's output buffer.
  531. IN_COMMON int nbuf_length; // Maximum of above, for breaking.
  532. EXTERN boolean out_at_beginning SET(YES); // Flag for the output buffer.
  533.  
  534. @ Send a character to \Fortran's output (buffered).  Possibly against the
  535. general philosophy of \WEB, here we make some attempt to make the output
  536. readable by indenting loop structures.  (It's not clear the indentation
  537. scheme has been adequately tested when the level is very deep.)
  538.  
  539. @<Part 1@>=@[
  540.  
  541. SRTN buffer_out FCN((c))
  542.     outer_char c C1("Output this character to the \Fortran\ buffer.")@;
  543. {
  544. outer_char *px; // For |in_string| |meta_mode| processing.
  545.  
  546. @<|switch| for single character output to \Fortran@>@;
  547.  
  548. /* When a statement label ends, skip to column~7. */
  549. if(is_label && !isdigit(c) )
  550.     {
  551.     is_label = NO;
  552.     out_pos = 6 + indent_level*INDENT_SIZE;
  553.     if(c==':' || c==' ') return; // Throw away the trailing colon.
  554.     }
  555.  
  556. @<Possibly number |do|s@>@;
  557.  
  558. /* Can't put it off any longer: Put the character into the buffer. */
  559. last_out = outp_buf[out_pos++] = c;
  560.  
  561. return;
  562. }
  563.  
  564. @ Not every character fired at |buffer_out| should actually be printed on
  565. the output file; some are special flags.
  566.  
  567. @<|switch| for single char...@>=
  568.  
  569. switch(c)
  570.     {
  571. case '\0': if(!in_string) return;  // In case a null sneaks in, ignore it.
  572.  
  573. /* Reset the verbatim comment mode. We have to remember whether we were in
  574. the middle of a line; if we were, we must resume continuation mode. */
  575.     if(in_string && started_vcmnt) 
  576.         {
  577.         NEWLINE_TO_FORTRAN(should_continue);
  578.         started_vcmnt = NO;
  579.         return;
  580.         }
  581.     break;
  582.  
  583. case '{':
  584. case '}':
  585. /* Filter out braces from \Ratfor. */
  586.     if(!in_string && xpn_Ratfor) return;
  587.     break;
  588.  
  589.  /* Ignore any blanks at beginning of line. */
  590. case ' ':
  591.     if(out_at_beginning) return;
  592.     break;
  593.  
  594. @t\4@>@<Case for newline@>@;
  595.  
  596. /* Semicolons not in strings mean emit a new line (except when they were
  597. earlier translated into |semi| during stringizing). */
  598. case ';':
  599.   if(!(in_string || in_constant))
  600.     {
  601.     NEWLINE_TO_FORTRAN(NOT_CONTINUATION);
  602.     return;
  603.     }
  604.    break;
  605.  
  606. case interior_semi:
  607. case semi:
  608.     c = ';'; @+ break;
  609.  
  610.  /* Handle meta-comments. */
  611. case begin_meta:
  612.     if(!meta_mode && last_out != '\n') flush_out(YES);
  613.     meta_mode = YES;
  614.     if(in_string)
  615.         { /* Start standard meta-comment. */
  616.         TO_BUFFER(top);
  617.         if(out_pos > 0) flush_out(YES);
  618.         }
  619.     rst_out(NOT_CONTINUATION);
  620.     return;
  621.  
  622. case end_meta:
  623.     if(in_string)
  624.         { /* Finish standard meta-comment. */
  625.         TO_BUFFER(bottom);
  626.         if(out_pos > 0) flush_out(YES);
  627.         started_vcmnt = NO;
  628.         }
  629.     else flush_out(YES);
  630.  
  631.     rst_out(NOT_CONTINUATION);
  632.     return;
  633.    }
  634.  
  635. /* If we're still going at column 73, emit a new line and make the next
  636. line a continuation line. */
  637. if(out_pos >= nbuf_length)
  638.     {
  639.     if(free_Fortran) outp_buf[out_pos++] = '&'; // Standard F--90 contin.
  640.     flush_out(YES);
  641.     rst_out(CONTINUATION);    /* Continuation. */
  642.  
  643.     if(in_string && started_vcmnt) @<Begin verbatim comment line@>;
  644.     }
  645.  
  646. if(out_at_beginning)
  647.     {
  648.     out_at_beginning = NO;
  649.  
  650. /* Statement labels require special treatment. When we sense one, we raise
  651. a special flag and put them into column~1. */
  652.     if(!in_string)
  653.            if(isdigit(c) && !is_label)
  654.         {
  655.         is_label = YES;
  656.         out_pos = 0;
  657.         }
  658.        else if(c=='#') 
  659.         { /* Place the \&{\#line} command in column~1. */
  660.         outp_buf[0] = (outer_char)CHOICE(free_90,
  661.             begin_comment_char[lan_num(out_language)], '*');
  662. // Treat as comment. 
  663.         out_pos = 1;
  664.         return;
  665.         }
  666.     }
  667.  
  668. @ Processing a newline is somewhat annoying because of the need to handle
  669. verbatim comments. The logic could be cleaned up here, but since it
  670. permeates the entire code, don't try it.
  671.  
  672. @<Case for newline@>=
  673.  
  674. case '\n':
  675.     if(in_cdir) 
  676.         {
  677.         out_pos = 0;
  678.         }
  679.     else if(!in_string || (in_string && started_vcmnt) )
  680.         {
  681.         NEWLINE_TO_FORTRAN(NOT_CONTINUATION);
  682.  
  683.         if(in_string && started_vcmnt)@<Begin verbatim comment line@>@;
  684.         }
  685.     else if(!started_vcmnt)
  686.         { /* Remember if  there's stuff in the buffer. If so, when
  687. we terminate the verbatim comment we must continue. */
  688.         should_continue = BOOLEAN(out_pos > rst_pos); 
  689.  
  690. /* The next statement prevents overwriting the stuff already in the buffer. */
  691.         if(should_continue) {NEWLINE_TO_FORTRAN(NOT_CONTINUATION);}
  692.         should_continue = BOOLEAN((!free_Fortran) && should_continue);
  693.         @<Begin verbatim c...@>;
  694.         started_vcmnt = YES;
  695.         }
  696.  
  697.     return;
  698.  
  699.  
  700. @ The following stuff, which implements the \.{-d}~option, is kludgy and
  701. obsolete; use \Ratfor\ instead. 
  702.  
  703. @<Possibly number |do...@>=
  704.  
  705. if(number_dos && !continuation_line && (language==FORTRAN ||
  706.         language==FORTRAN_90 || R66) ) 
  707.     {
  708.     outer_char HUGE *do_pos;
  709.  
  710.     do_pos = outp_buf + 6;
  711.  
  712.     if(out_pos == 9)
  713.         {
  714.         if(STRNCMP(do_pos,"do ",3)==0 && !isdigit(c))
  715.             {
  716.             sprintf((char *)(do_pos+=3),"%lu ",
  717.                 stmt_num[do_level++] = max_stmt++);
  718.  
  719.             while(*do_pos++ != '\0') out_pos++;
  720.             }
  721.         }
  722.     else if( (out_pos==10 && STRNCMP(do_pos,"endd",4)==0) ||
  723.             (out_pos==11 && STRNCMP(do_pos,"end d",5)==0) )
  724.         {
  725.         if(do_level == 0)
  726.             {
  727.             ERR_PRINT(T,"Too many END DOs");
  728.             *outp_buf = 'C';
  729.             }
  730.         else
  731.             {
  732.             sprintf((char *)outp_buf,"%-5lu CONTINUE",
  733.                 stmt_num[--do_level]); 
  734.             out_pos = 14;
  735.             return;
  736.             }
  737.         }
  738.     }
  739.  
  740. @ Handle a newline when the output language is \Fortran.
  741.  
  742. @d NEWLINE_TO_FORTRAN(continuation_flag)
  743.     flush_out(YES); // Write out the buffer.
  744.     rst_out(continuation_flag)@; /* Reinitialize the buffer with no
  745. continuation character. */
  746.  
  747. @ The following is used during output of verbatim comments.
  748. @<Begin verbatim c...@>=
  749. {
  750. int k;
  751.  
  752. if(!meta_mode)
  753.     {
  754.     outp_buf[0] = begin_comment_char[lan_num(out_language)];
  755.  
  756.     for(out_pos = 1,k=spcs_after_cmnt; k; k--)
  757.         outp_buf[out_pos++] = ' ';
  758.     }
  759.  
  760. nbuf_length = MAX(t_style.output_line_length,80);
  761. out_at_beginning = NO; // Prevents stripping off blanks at beginning of cmnt.
  762. }
  763.  
  764. @ This routine writes out the current contents of \Fortran's output buffer.
  765. @<Part 1@>=@[
  766.  
  767. SRTN flush_out FCN((prn_new_line))
  768.     boolean prn_new_line C1("Do we print a newline?")@;
  769. {
  770. outp_buf[out_pos] = '\0'; // Terminate the buffer.
  771.  
  772. /* Dump it out, followed by a newline. */ 
  773. WRITE1(outp_buf,out_pos)@;
  774.  
  775. if(prn_new_line) 
  776.     {
  777.     PUTC(last_out='\n');
  778.     flush0();
  779.     }
  780. }
  781.  
  782. @ After we've flushed the buffer, we must prepare it for the next stuff.
  783.  
  784. @d TO_BUFFER(type)
  785.     if(!nuweb_mode)
  786.         {
  787.         px = t_style.meta[lan_num(language)].msg.type;
  788.         STRCPY(outp_buf,px);
  789.         out_pos = STRLEN(px);
  790.         }
  791.  
  792. @<Part 1@>=@[
  793.  
  794. int rst_out FCN((continuation))
  795.     boolean continuation C1("Is line a continuation?")@;
  796. {
  797. if(!continuation) RST_LAST_EXPR@; /* Reset the pointer so we can remember the
  798.                     upcoming expression. */
  799.  
  800. /* Blank out the comment and label field (first five columns). */
  801. for(out_pos=0; out_pos<5; ++out_pos)
  802.     outp_buf[out_pos] = ' ';
  803.  
  804. /* Deposit the continuation character. */
  805. outp_buf[out_pos++] = continuation ? t_style.cchar : (outer_char)' ';
  806. continuation_line = continuation;
  807. out_at_beginning = BOOLEAN(!continuation_line);
  808.  
  809. nbuf_length = t_style.output_line_length;
  810.  
  811. if(meta_mode) 
  812.     {
  813.     if(!in_string)
  814.         { /* Error message. */
  815.         outp_buf[0] = begin_comment_char[lan_num(out_language)];
  816.         if(!xpn_Ratfor) out_pos = 1 + spcs_after_cmnt;
  817.         }
  818.  
  819.     nbuf_length = MAX(nbuf_length,80);
  820.     }
  821.  
  822. /* If it's not a continuation line, mark the beginning. Also, if we're in a
  823. loop, indent appropriately. */
  824. if(out_at_beginning && xpn_Ratfor) blank_out(indent_level);
  825.  
  826. return rst_pos = out_pos;
  827. }
  828.  
  829. @ Blank out columns appropriate to |indent_level|.
  830. @<Part 1@>=@[
  831.  
  832. SRTN blank_out FCN((n))
  833.     int n C1("Number of levels to indent.")@;
  834. {
  835. outer_char HUGE *p;
  836. int i;
  837.  
  838. for(i=0,p=outp_buf+out_pos; i < n*INDENT_SIZE; i++) *p++ = ' ';
  839.  
  840. out_pos += i;
  841. rst_pos = out_pos;
  842. }
  843.  
  844. @*1 Index reversal.  For \Fortran\ programming, the \.{-)} option turns on
  845. \It{index reversal}.  It converts constructions of the form `\.{a(k)(i)}'
  846. to `\.{a(i,k)}', `\.{a(k(1)(2))(j)}' to `\.{a(j,k(2,1))}'.  As evidenced by
  847. this last example, the procedure must be recursive.  It works as follows.
  848. When a left parenthesis is recognized, the parenthesis level is advanced.
  849. Output tokens are copied into a temporary buffer.  If the combination
  850. `\.{)(}' is recognized, the buffer level is advanced and tokens are copied
  851. into the new buffer.  This continues until a right parenthesis is
  852. recognized.  Then the buffer levels are copied in reverse order to the
  853. buffer of the previous parenthesis level, with commas inbetween.
  854.  
  855. The annoyance is how to treak `\.{)(}'.  There's no room for more tokens;
  856. furthermore, the combination might be produced by macro processing.
  857. Therefore, what actually happens when a right paren is seen is that a flag
  858. |rparen| is set.  The buffers are not actually unwound at this time, but
  859. deferred until the next character, where it can be decided whether `\.{)(}'
  860. has occurred.  This is necessary because the output scheme cannot
  861. conveniently look ahead; bytes are sent to |C_PUTC| one at a time.  The
  862. disadvantage of this scheme is that white space sneaking inbetween the
  863. parens will prevent the `\.{)(}' from being recognized (with the current
  864. logic). 
  865.  
  866. @d CUR_BUF (pai->text_buf[pai->ilevel])
  867.  
  868. @<Typedef...@>=
  869.  
  870. /* We'll manage the buffers with a structure.  That way, we can use a
  871. standard routine |store| to add a byte. */
  872. typedef struct
  873.     {
  874.     outer_char HUGE *start, HUGE *pos, HUGE *end;
  875.     } TEXT_BUF;
  876.  
  877. /* One parenthesis level is described like this. */
  878. typedef struct
  879.     {
  880.     int ilevel;    // Current buffer (index) level.
  881.     TEXT_BUF HUGE *text_buf[10]; // Temporary storage for the index tokens.
  882.     TEXT_BUF HUGE *last_buf; // Buffer of the previous level.
  883.     } PAREN_LEVEL;
  884.  
  885. PAREN_LEVEL paren_level[10], HUGE *pai = paren_level;
  886.  
  887. int rparen = NO; // Was the last character a right paren?
  888.  
  889.  
  890. @
  891. @<Allocate dyn...@>=
  892. {
  893. pai->ilevel = 0;
  894. pai->text_buf[0] = pai->last_buf = calloc(1, sizeof(TEXT_BUF));
  895. }
  896.  
  897. @ Completed index levels are written into the appropriate |TEXT_BUF|, which
  898. is initialized if necessary.  If we're at parenthesis level~0, we don't
  899. store, but fire the byte at the \Fortran\ output buffer.
  900.  
  901. @<Part 1@>=@[
  902.  
  903. SRTN store FCN((t, c))
  904.     TEXT_BUF HUGE *t C0("")@;
  905.     outer_char c C1("")@;
  906. {
  907. if(pai == paren_level || t == paren_level[0].last_buf)
  908.     { /* Send directly to \Fortran's output buffer. */
  909.     buffer_out(c);
  910.     return;
  911.     }
  912.  
  913. /* Store in the indicated text buffer; initialize that if necessary. */
  914. if(t->start == NULL)
  915.     {
  916.     t->pos = t->start = calloc(100, 1);
  917.     t->end = t->start + 100;
  918.     }
  919.  
  920. *t->pos++ = c;
  921. }
  922.  
  923. @ Here we unwind the index entries in reverse order, interspersing them by
  924. commas.  Unwinding one buffer entry just means copying it into the
  925. |last_buf|. 
  926.  
  927. @<Part 1@>=@[
  928.  
  929. SRTN unwind(VOID)
  930. {
  931. int i;
  932. TEXT_BUF HUGE *t;
  933. outer_char HUGE *s1;
  934.  
  935. if(pai == paren_level)
  936.     {
  937.     ERR_PRINT(T, "Missing '('");
  938.     buffer_out(')');
  939.     return;
  940.     }
  941.  
  942. for(i=pai->ilevel; i >= 0; i--)
  943.     {
  944.     t = pai->text_buf[i];
  945.  
  946.     for(s1=t->start; s1<t->pos; s1++)
  947.         store(pai->last_buf, *s1);
  948.  
  949.     t->pos = t->start; // Reset the buffer.
  950.  
  951.     if(i > 0)
  952.         store(pai->last_buf, ',');
  953.     }
  954.  
  955. store(pai->last_buf, ')');
  956. pai--; // Decrement parenthesis level.
  957. }
  958.  
  959.  
  960. @ The following code is pressed into service with the `\.{-)}' flag (and
  961. when one is not inside a character string).
  962.  
  963. @<Reverse \Fortran\ indices@>=
  964. {
  965. switch(c)
  966.     {
  967.    case '(':
  968.     if(rparen)
  969.         { /* The combination `\.{)(}' has occured; advance the
  970. buffer level. */
  971.         pai->ilevel++;
  972.  
  973.         @<Allocate |CUR_BUF| if necessary@>@;
  974.             
  975.         rparen = NO;
  976.         }
  977.     else
  978.         { /* Time for a new parenthesis level.  Put the parenthesis
  979. into the old level.  Remember where that was, then advance the level. */
  980.         store(CUR_BUF, '(');
  981.  
  982.         (pai+1)->last_buf = CUR_BUF;
  983.         pai++;
  984.         pai->ilevel = 0;
  985.  
  986.         @<Allocate |CUR_BUF|...@>@;
  987.         }
  988.  
  989.     break;
  990.  
  991.    case ')':
  992.     if(!rparen)
  993.         rparen = YES;
  994.     else
  995.         unwind();
  996.  
  997.     break;
  998.  
  999.    default:
  1000.     if(rparen)
  1001.         {
  1002.         unwind();
  1003.         rparen = NO;
  1004.         }
  1005.  
  1006.     store(CUR_BUF, c);
  1007.     break;
  1008.     }
  1009. }
  1010.  
  1011. @
  1012. @<Allocate |CUR_BUF|...@>=
  1013. {
  1014. if(!CUR_BUF)
  1015.     CUR_BUF = GET_MEM("CUR_BUF", 1, TEXT_BUF);
  1016. }
  1017.  
  1018. @i texts.hweb
  1019.  
  1020.  
  1021. @ Allocate the principal arrays.
  1022. @<Allocate dyn...@>=
  1023.  
  1024. alloc_Rat(); // Allocate \Ratfor\ arrays.
  1025.  
  1026. ALLOC(text,text_info,ABBREV(max_texts),max_texts,0);
  1027. text_end = text_info + max_texts - 1;
  1028.  
  1029. ALLOC(text,txt_dinfo,ABBREV(dtexts_max),dtexts_max,0);
  1030. textd_end = txt_dinfo + dtexts_max - 1;
  1031.  
  1032. ALLOC(eight_bits,tok_mem,ABBREV(max_toks_t),max_toks,0);
  1033. tok_m_end = tok_mem + max_toks - 1;
  1034.  
  1035. ALLOC(eight_bits,tok_dmem,ABBREV(max_dtoks),max_dtoks,0);
  1036. tokd_end = tok_dmem + max_dtoks - 1;
  1037.  
  1038. @ The convention is that the first entry, relating to the unnamed module,
  1039. has no replacement text. (The |CAST| operation was necessary to make the
  1040. Aztec compiler happy. Maybe it's not necessary anymore since we switched to
  1041. dynamic allocation.)
  1042. @<Set init...@>=
  1043.  
  1044. CAST(text_pointer,text_info)->tok_start = tok_ptr = tok_mem;
  1045. CAST(text_pointer,txt_dinfo)->tok_start = tok_dptr = tok_dmem;
  1046.  
  1047.   /* This makes replacement text 0 of length zero. */
  1048. text_ptr = text_info+1; text_ptr->tok_start = tok_mem;
  1049. txt_dptr = txt_dinfo + 1; txt_dptr->tok_start = tok_dmem;
  1050.  
  1051. @ If |p| is a pointer to a module name, |p->equiv| is a pointer to its
  1052. replacement text, an element of the array |text_info|.
  1053.  
  1054. @ The undefined module has no replacement text.
  1055.  
  1056. @<Set init...@>=
  1057.  
  1058. CAST(name_pointer,name_dir)->equiv = (EQUIV)text_info; 
  1059.  
  1060. @ Here's the procedure that decides whether a name of length |l|
  1061. starting at position |first| equals the identifier pointed to by |p|:
  1062.  
  1063. @<Part 1@>=@[
  1064.  
  1065. boolean names_match FCN((p,first,l,dummy))
  1066.     name_pointer p C0("Points to the proposed match.")@;
  1067.     CONST ASCII HUGE *first C0("Position of first character of string.")@;
  1068.     int l C0("length of identifier.")@;
  1069.     eight_bits dummy C1("Not used here")@;
  1070. {
  1071.   if (length(p)!=l) return NO;
  1072.   return (boolean)(!STRNCMP(first,p->byte_start,l));
  1073. }
  1074.  
  1075. @ The |ini_node| operation differs for \FTANGLE\ and \FWEAVE.
  1076. @<Part 1@>=@[
  1077.  
  1078. SRTN ini_node FCN((node))
  1079.     CONST name_pointer node C1("")@;
  1080. {
  1081. node->equiv=(EQUIV)text_info;
  1082. @<Initialize |mod_info| and |Language|@>@;
  1083. }
  1084.  
  1085. @ Several procedures are called only by \.{WEAVE}, but null routines need
  1086. to be here so the linker doesn't complain.
  1087.  
  1088. @<Part 1@>=@[
  1089.  
  1090. SRTN ini_p FCN((p,t))
  1091.     name_pointer p C0("")@;
  1092.     eight_bits t C1("")@;
  1093. {}
  1094.  
  1095. SRTN open_tex_file(VOID)
  1096. {}
  1097.  
  1098. @* TOKENS.  Replacement texts, which represent code in a compressed format,
  1099. appear in |tok_mem| as mentioned above. The codes in these texts are called
  1100. `tokens'; some tokens occupy two consecutive eight-bit byte positions, and
  1101. the others take just one byte.
  1102.  
  1103. If $p$ points to a replacement text, |p->tok_start| is the |tok_mem|
  1104. position of the first eight-bit code of that text. If |p->text_link=macro
  1105. == 0|, this is the replacement text for a macro, otherwise it is the
  1106. replacement text for a module. In the latter case |p->text_link| is either
  1107. equal to |module_flag|, which means that there is no further text for this
  1108. module, or |p->text_link| points to a continuation of this replacement
  1109. text; such links are created when several modules have texts with the same
  1110. name, and they also tie together all the texts of unnamed modules.  The
  1111. replacement text pointer for the first unnamed module appears in
  1112. |text_info->text_link|, and the most recent such pointer is |last_unnamed|.
  1113.  
  1114. @d module_flag (sixteen_bits)max_texts /* Final |text_link| in module
  1115.                         replacement texts. */ 
  1116.  
  1117. @<Glob...@>=
  1118.  
  1119. EXTERN text_pointer last_unnamed; /* Most recent replacement text of
  1120.                     unnamed module. */ 
  1121.  
  1122. @<Set init...@>=
  1123.  
  1124. last_unnamed = text_info; // Root of the unnamed module.
  1125. CAST(text_pointer,text_info)->text_link = 0; // No unnamed pieces yet.
  1126.  
  1127. @ The following procedure is used to enter a two-byte value into
  1128. |tok_mem| when a replacement text is being generated.
  1129.  
  1130. @<Part 1@>=@[
  1131.  
  1132. SRTN store_two_bytes FCN((x))
  1133.     sixteen_bits x C1("Two-byte token to be entered into |tok_mem|.")@;
  1134. {
  1135.   if (tok_ptr+2>tok_m_end) OVERFLW("tokens",ABBREV(max_toks_t));
  1136.  
  1137.   *tok_ptr++ = (eight_bits)(x >> 8); // Store high byte.
  1138.   *tok_ptr++ = (eight_bits)(x & 0377); // Store low byte.
  1139. }
  1140.  
  1141. @i stacks.hweb
  1142.  
  1143. @ Dynamically allocate the stack.
  1144. @<Allocate dyn...@>=
  1145.  
  1146. ALLOC(output_state,stack,ABBREV(stck_size_t),stck_size,1);
  1147. stck_end = stack + stck_size; // End of |stack|.
  1148.  
  1149. @ To get the output process started, we will perform the following
  1150. initialization steps. We may assume that |text_info->text_link| is nonzero,
  1151. since it points to the \cee\ text in the first unnamed module that generates
  1152. code; if there are no such modules, there is nothing to output, and an
  1153. error message will have been generated before we do any of the initialization.
  1154.  
  1155. @d UNNAMED_MODULE 0
  1156.  
  1157. @<Initialize the output stacks@>=
  1158.  
  1159. stck_ptr = stack+1; cur_name = name_dir; 
  1160. cur_repl = CAST(text_pointer,text_info)->text_link + text_info;
  1161. cur_byte = cur_repl->tok_start; cur_end = (cur_repl+1)->tok_start;
  1162. cur_mod = UNNAMED_MODULE; 
  1163. params = cur_params = cur_global_params = global_params;
  1164. frz_params();
  1165.  
  1166. @ When the replacement text for name~|p| is to be inserted into the output,
  1167. the following subroutine is called to save the old level of output and get
  1168. the new one going.
  1169.  
  1170. We assume that the C compiler can copy structures.  (Certainly true for ANSI.)
  1171. @^system dependencies@>
  1172.  
  1173. @<Part 1@>=@[
  1174.  
  1175. SRTN push_level FCN((p,b0,b1))
  1176.     name_pointer p C0("The new replacement text.")@;
  1177.     CONST eight_bits HUGE *b0 C0("If |p == NULL|, beginning of new \
  1178. stuff in memory.")@; 
  1179.     CONST eight_bits HUGE *b1 C1("If |p == NULL|, end of new stuff in \
  1180. memory.")@; 
  1181. {
  1182. if(stck_ptr==stck_end) OVERFLW("stack levels",ABBREV(stck_size_t));
  1183.  
  1184. *stck_ptr = cur_state; // Save old state.
  1185.  
  1186. /* Initialize new state. */
  1187. cur_name = p;
  1188.  
  1189. if(p != NULL)
  1190.     {
  1191.     cur_repl = (text_pointer)p->equiv;
  1192.  
  1193.     if(cur_repl == NULL) CONFUSION("push_level","cur_repl is NULL");
  1194.  
  1195.     cur_byte = cur_repl->tok_start; 
  1196.     cur_end = (cur_repl+1)->tok_start;
  1197.     }
  1198. else
  1199.     {
  1200.     cur_repl = NULL;
  1201.  
  1202.     cur_byte = (eight_bits HUGE *)b0; cur_end = (eight_bits HUGE *)b1;
  1203.     new_mbuf(); // Allocate new macro buffer. See \.{macs.web}.
  1204.     }
  1205.  
  1206. /* Get the language for this module. All modules start off in the global
  1207. language for that module. Also, the old state needs to recall the local
  1208. language switch. */
  1209. (stck_ptr++)->params = cur_params = cur_global_params =
  1210.     (p != NULL) ? params : params; /* ??? */
  1211. set_output_file(cur_language);
  1212. cur_mod = UNNAMED_MODULE; // Assume this until told otherwise.
  1213. }
  1214.  
  1215. @ When we come to the end of a replacement text, the |pop_level| subroutine
  1216. does the right thing: It either moves to the continuation of this replacement
  1217. text or returns the state to the most recently stacked level. If the pop
  1218. was successful---i.e., if there's more stuff to come---|YES| is returned.
  1219.  
  1220. @<Part 1@>=@[
  1221.  
  1222. boolean pop_level(VOID) /* do this when |cur_byte| reaches |cur_end| */
  1223. {
  1224. if(cur_repl != NULL && cur_repl->text_link < module_flag) 
  1225.     { /* Link to a continuation---i.e., the next in the chain of
  1226. replacement texts for this module. */
  1227.     cur_repl = cur_repl->text_link + text_info; // Stay on the same level.
  1228.     cur_byte = cur_repl->tok_start; 
  1229.     cur_end = (cur_repl+1)->tok_start;
  1230.  
  1231. /* In case we changed languages during the module, localize the change. */
  1232.     if(cur_repl->module_text)
  1233.         {
  1234.         params = cur_params = cur_global_params;
  1235.         frz_params();
  1236.         set_output_file(cur_language);
  1237.         }
  1238.  
  1239.     return YES;
  1240.     }
  1241.  
  1242. stck_ptr--; // Go down to the previous level.
  1243.  
  1244. if (stck_ptr>stack) 
  1245.     {
  1246.     cur_state = *stck_ptr; // Copy the current state structure.
  1247.     set_output_file(cur_language);
  1248.     return YES; // Successfully descended to a lower active level.
  1249.     }
  1250.  
  1251. return NO; // Already at lowest level (top of stack).
  1252. }
  1253.  
  1254. @ The heart of the output procedure is the |get_output| routine, which
  1255. produces the next token of output that is not a reference to a macro. This
  1256. procedure handles all the stacking and unstacking that is necessary.  It
  1257. returns the value |module_number| if the next output begins or ends the
  1258. replacement text of some module, in which case |cur_val| is that module's
  1259. number (if beginning) or the negative of that value (if ending). (A module
  1260. number of 0 indicates not the beginning or ending of a module, but a
  1261. \&{\#line} command.)  And it returns the value |identifier| if the next
  1262. output is an identifier of length two or more, in which case |cur_val|
  1263. points to that identifier name.
  1264.  
  1265. @<Global...@>=
  1266.  
  1267. /* These harmlessly redefine stuff in \.{typedefs.web}. It's a bit shaky, but
  1268. it seems to work. One was running out of lower-order tokens. */
  1269.  
  1270. #undef begin_format_stmt
  1271. #define begin_format_stmt OCTAL(14)
  1272.  
  1273. #undef end_format_stmt
  1274. #define end_format_stmt OCTAL(15)
  1275.  
  1276. EXTERN long cur_val; /* Additional information corresponding to output
  1277.     token. This must be \It{signed} (and capable of handling a full
  1278.     |sixteen_bits|) because of trickery involving output of the module
  1279.     numbers. */ 
  1280.  
  1281. @ If |get_output| finds that no more output remains, it returns the
  1282. value~|NO|. Otherwise, it returns the next token after macro expansion.
  1283.  
  1284. @<Part 1@>=@[
  1285. eight_bits get_output(VOID) 
  1286. {
  1287. sixteen_bits a; // Value of current byte.
  1288.  
  1289. restart: 
  1290.   if (stck_ptr==stack) return NO; // At top of stack; nothing more.
  1291.  
  1292.   if (cur_byte==cur_end) 
  1293.     {
  1294.     cur_val = -((long)cur_mod); /* When we end a module, |cur_val| is
  1295. set to the negative of the module number.  The cast is needed because of
  1296. sign extension. */
  1297.  
  1298.     if(cur_val != ignore) OUT_CHAR(module_number); /* Do this here so
  1299. it gets into the right file if we're changing languages. */
  1300.  
  1301.     pop_level();
  1302.  
  1303.     if (cur_val==ignore) goto restart;
  1304.  
  1305.     return module_number;
  1306.     }
  1307.  
  1308. @<Expand output byte@>@;
  1309. }
  1310.  
  1311. @ To get the saved stuff out, we need a slightly different version of the
  1312. |get_output| routine.
  1313. @<Part 1@>=@[
  1314.  
  1315. eight_bits get_saved_output FCN((stck_ptr0))
  1316.     stack_pointer stck_ptr0 C1("")@;
  1317. {
  1318. sixteen_bits a;
  1319.  
  1320. restart:
  1321. if(stck_ptr == stack || stck_ptr != stck_ptr0) return NO;
  1322.  
  1323. if(DONE_LEVEL)
  1324.     { /* Hunt for end-of-tokens mark. */
  1325.     if(!pop_level()) CONFUSION("get_saved_output",
  1326.         "Shouldn't encounter top level here");
  1327.     return ignore;
  1328.     }
  1329.  
  1330. @<Expand output byte@>@;
  1331. }
  1332.  
  1333. @ We will recover the saved stuff by pushing the stack ``by hand''. When
  1334. |is_expr| is true, we reset the pointer used to save expressions that
  1335. implement the two-token operators like `\.{*=}'. We also allocate a new
  1336. macro buffer on the stack, and switch to it, so that if macros are expanded
  1337. during the |copy_out|, things don't get overwritten. (This last stuff is
  1338. done by |push_level|.)
  1339. @<Part 1@>=@[
  1340.  
  1341. SRTN copy_out FCN((p0,p1,is_expr))
  1342.     CONST eight_bits HUGE *p0 C0("Start of memory buffer.")@;
  1343.     CONST eight_bits HUGE *p1 C0("End of memory buffer.")@;
  1344.     boolean is_expr C1("Flag for resetting pointer to last expression.")@;
  1345. {
  1346. stack_pointer stck_ptr0;
  1347.  
  1348. /* If we're copying out an expression, reset the memory pointer. */
  1349. if(is_expr) rst_last();
  1350.  
  1351. push_level(NULL,p0,p1);
  1352. stck_ptr0 = stck_ptr;
  1353. while(get_saved_output(stck_ptr0));
  1354. }
  1355.  
  1356. @ The character sent by |send_single|, below.
  1357. @<Glob...@>=
  1358.  
  1359. EXTERN eight_bits sent;
  1360.  
  1361. @ Occasionally, the next byte contains useful information. That's put into
  1362. |cur_val|, which can be processed by |out_char|.
  1363.  
  1364. @<Send a single-byte token, handling escapes such as
  1365. |begin_language| or |dot_const|@>=
  1366. {
  1367. send_single(a);
  1368. }
  1369.  
  1370. @  A function so we can interface to \.{rat77.web}.
  1371. @<Part 1@>=@[
  1372.  
  1373. SRTN send_single FCN((a))
  1374.     sixteen_bits a C1("")@;
  1375. {
  1376. boolean scope;
  1377.  
  1378. switch(a)
  1379.     {
  1380.    case begin_language:
  1381. /* |begin_language| escapes the actual language, which follows next. */
  1382.     switch(sent = *cur_byte++)
  1383.         {
  1384.        case NO_LANGUAGE: // Serves double-duty for |new_output_file|.
  1385.         scope = *cur_byte++;
  1386.         a = *cur_byte++;
  1387.         a = IDENTIFIER(a,*cur_byte++);
  1388.         new_out(scope,a);
  1389.         sent = new_output_file;
  1390.         break;
  1391.  
  1392.        @t\4@>@<Cases for appending a language switch@>;
  1393.  
  1394.        case NUWEB_OFF:
  1395.        case NUWEB_ON:
  1396.         nuweb_mode = BOOLEAN(0x0F & sent);
  1397.         break;
  1398.  
  1399.        case no_mac_expand:
  1400.         mac_protected = no_expand = YES;
  1401.         break;
  1402.         }
  1403.     break;
  1404.  
  1405.    case dot_const:
  1406.     cur_val = *cur_byte++; /* The relative number of the
  1407. operator is stored in the byte following |dot_const|. */
  1408.     sent = OUT_CHAR(a);
  1409.     break;
  1410.  
  1411.    default:
  1412.     sent = OUT_CHAR(a); // One-byte token.
  1413.     break;
  1414.     }
  1415. }
  1416.  
  1417. @ Open a new output file in response to an~\.{@@O} (global scope)
  1418. or~\.{@@o} (local scope) command.
  1419.  
  1420. @<Part 1@>=@[
  1421.  
  1422. #define TEMP_LEN (2*MAX_FILE_NAME_LENGTH)
  1423.  
  1424. SRTN new_out FCN((global_scope,a))
  1425.     boolean global_scope C0("0 for local, 1 for global")@;
  1426.     sixteen_bits a C1("")@;
  1427. {
  1428. name_pointer np = name_dir + a;
  1429. CONST ASCII HUGE *end;
  1430. size_t len;
  1431. outer_char temp_from[TEMP_LEN],temp_to[TEMP_LEN];
  1432. outer_char temp[MAX_FILE_NAME_LENGTH];
  1433.  
  1434. if(global_scope)
  1435.     {
  1436.     SPRINTF(TEMP_LEN,temp_from,
  1437.         `"\n\n  (This file was continued via @@O from %s.)",
  1438.         params.OUTPUT_FILE_NAME`);
  1439.     }
  1440. else 
  1441.     {
  1442.     SPRINTF(TEMP_LEN,temp_from," ");
  1443.     }
  1444.  
  1445. /* Extract the file name from the |name_dir|. */
  1446. PROPER_END(end);
  1447. len = end - np->byte_start;
  1448. STRNCPY(temp,np->byte_start,len);
  1449. TERMINATE(temp,len);
  1450. to_outer((ASCII HUGE *)temp);
  1451. new_fname(¶ms.OUTPUT_FILE_NAME,temp,NULL);
  1452.  
  1453.  
  1454. if(global_scope)
  1455.     { /* Write a continuation message into the old file. */
  1456.     new_fname(&global_params.OUTPUT_FILE_NAME,temp,NULL);
  1457.     SPRINTF(TEMP_LEN,temp_to,`"  (Continued via @@O to %s.)",
  1458.         params.OUTPUT_FILE_NAME`);
  1459.     OUT_MSG(to_ASCII(temp_to),NULL);
  1460.     close_out(out_file);
  1461.     }
  1462. else
  1463.     fflush(out_file);
  1464.  
  1465. open_out(temp_from,global_scope);
  1466. }
  1467.  
  1468. #undef TEMP_LEN
  1469.  
  1470. @ The next fragment is used both here and in the \Ratfor-77 output routine.
  1471. @<Expand output byte@>=
  1472. {
  1473. a = *cur_byte++;
  1474.  
  1475. if (TOKEN1(a)) 
  1476.     {
  1477.     @<Send a single-byte token...@>;
  1478.     return sent;
  1479.     }
  1480. else 
  1481.     {
  1482.     a = IDENTIFIER(a,*cur_byte++);
  1483.  
  1484.     switch (a/MODULE_NAME) 
  1485.         { 
  1486.        case 0: 
  1487.         cur_val = a; 
  1488.         @<Check for wild \Ratfor\ scan@>@;
  1489.         return OUT_CHAR(identifier);
  1490.  
  1491.        case 1: 
  1492.         @<Expand module |a-MODULE_NAME|@>@;
  1493.         goto restart;
  1494.  
  1495.        default: 
  1496.         cur_val = a - MODULE_NUM; 
  1497.         if (cur_val>UNNAMED_MODULE) cur_mod = (sixteen_bits)cur_val;
  1498. /* Remember the current module so it can be used in
  1499. |out_char(module_number)| just after popping this level. */
  1500.         return OUT_CHAR(module_number); /* Module number at
  1501. beginning  of module. */
  1502.         }
  1503.     }
  1504. }
  1505.  
  1506. @ When checking for an out-of-control \Ratfor\ scan, we must look for the
  1507. following tokens:
  1508.  
  1509. @<Glob...@>=
  1510.  
  1511. IN_RATFOR sixteen_bits id_function,id_program,id_subroutine;
  1512.  
  1513. @ An errant \Ratfor\ scan can be stopped by looking for the beginning of
  1514. functions. 
  1515.  
  1516. @<Check for wild \Ratfor...@>=
  1517. {
  1518. IN_RATFOR boolean balanced;
  1519. IN_RATFOR ASCII cur_delim;
  1520.  
  1521. if(!balanced && language==RATFOR &&
  1522.         (a == id_function || a == id_program || a==id_subroutine))
  1523.     {
  1524.     RAT_ERROR(ERROR,"Inserted missing '%c' at beginning of function",
  1525.         1,XCHR(cur_delim));
  1526.     cur_byte -= 2; // Process the current identifier again.
  1527.     return OUT_CHAR(cur_delim); // Insert delimiter being searched for.
  1528.     }
  1529. }
  1530.  
  1531. @ When we expand a module, we remember the value for possible use in the
  1532. |_MODULE_NAME| macro.
  1533.  
  1534. @<Glob...@>=
  1535.  
  1536. EXTERN sixteen_bits cur_mod_no SET(0);
  1537.  
  1538. @ Implement the \.{\_MODULE\_NAME} built-in.
  1539.  
  1540. @<Define internal macros@>=
  1541.  
  1542. SAVE_MACRO("_MODULE_NAME $STRING($$MODULE_NAME)");
  1543. SAVE_MACRO("$MODULE_NAME $STRING($$MODULE_NAME)");
  1544.  
  1545. @
  1546. @d UNNAMED_MOD "unnamed"
  1547. @<Part 1@>=@[
  1548.  
  1549. SRTN i_mod_name_ FCN((n,pargs))
  1550.     int n C0("")@;
  1551.     PARGS pargs C1("")@;
  1552. {
  1553. int len;
  1554. name_pointer np = cur_name;
  1555. eight_bits HUGE *p;
  1556.  
  1557. CHK_ARGS("$MODULE_NAME",0);
  1558.  
  1559. if(cur_name) cur_mod_no = np - name_dir;
  1560. else cur_mod_no = 0;
  1561.  
  1562. len = cur_mod_no ? length(np) : STRLEN(UNNAMED_MOD);
  1563. MCHECK(len,"current module name");
  1564.  
  1565. if(cur_mod_no)
  1566.     for(p=np->byte_start; p<(np+1)->byte_start; )
  1567.         *mp++ = *p++;
  1568. else
  1569.     {
  1570.     STRCPY(mp,UNNAMED_MOD);
  1571.     to_ASCII(mp);
  1572.     mp += len;
  1573.     }
  1574. }
  1575.  
  1576. @ Here's the number corresponding to the current module name.
  1577. @<Part 1@>=@[
  1578.  
  1579. SRTN i_sect_num_ FCN((n,pargs))
  1580.     int n C0("")@;
  1581.     PARGS pargs C1("")@;
  1582. {
  1583. num_to_mbuf(n,pargs,"$SECTION_NUM",0,"section number",cur_mod);
  1584. }
  1585.  
  1586. @ The user may have forgotten to give any code text for a module name,
  1587. or the code text may have been associated with a different name by mistake.
  1588.  
  1589. @<Expand module |a-...@>=
  1590. {
  1591. name_pointer np;
  1592.  
  1593. a -= MODULE_NAME;
  1594.  
  1595. np = name_dir + a;
  1596.  
  1597. if(np->equiv != (EQUIV)text_info) push_level(np,NULL,NULL); 
  1598. else if(a != UNNAMED_MODULE) 
  1599.     { /* Module definition missing. */
  1600.     SET_COLOR(error);
  1601.     printf("\n! Not present: <"); prn_id(np); ERR_PRINT(NULL,">");
  1602. @.Not present: <section name>@>
  1603.     SET_COLOR(ordinary);
  1604.     @<Output a function call for debugging purposes@>@;
  1605.     }
  1606. }
  1607.  
  1608. @ When a missing module is detected, the command `\.{\$STUB(\It{name})}' is
  1609. inserted.  That macro expands by default to a function call appropriate to
  1610. the current language.
  1611.  
  1612. @<Define internal macros@>=
  1613.  
  1614. SAVE_MACRO("_STUB(s)$IFCASE($LANGUAGE_NUM,\
  1615. {missing_mod(#s);},{missing_mod(#s);},\
  1616. call nomod(#s),call nomod(#s),\
  1617. call nomod(#s),call nomod(#s),\
  1618. \\missingmod{s},\
  1619. %nomod(s),%nomod(s))");
  1620.  
  1621. SAVE_MACRO("$STUB(s)$IFCASE($LANGUAGE_NUM,\
  1622. {missing_mod(#s);},{missing_mod(#s);},\
  1623. call nomod(#s),call nomod(#s),\
  1624. call nomod(#s),call nomod(#s),\
  1625. \\missingmod{s},\
  1626. %nomod(s),%nomod(s))");
  1627.  
  1628. @ Here we build the tokenized text to make a call to a stub routine that
  1629. serves as the text of an undefined module.
  1630. @<Output a function call for debugging...@>=
  1631. {
  1632. #define TEMP_LEN 300
  1633.  
  1634. eight_bits temp[TEMP_LEN],temp1[TEMP_LEN];
  1635. sixteen_bits stub;
  1636. size_t n = length(np);
  1637.  
  1638. id_first = x__to_ASCII(OC("$STUB"));
  1639. stub = ID_NUM(id_first,id_first+5);
  1640.  
  1641. STRNCPY(temp1,np->byte_start,n);
  1642. temp1[n] = '\0';
  1643.  
  1644. SPRINTF(TEMP_LEN,temp,`"%c%c%c%c%s%c%c",
  1645.     LEFT(stub,ID0),RIGHT(stub),@'(',stringg,temp1,stringg,@')'`);
  1646. push_level(NULL,temp,temp+STRLEN(temp));
  1647.  
  1648. #undef TEMP_LEN
  1649. }
  1650.  
  1651. @ Interface to \.{rat77.web}.
  1652. @<Part 1@>=@[
  1653.  
  1654. SRTN x_mod_a FCN((a))
  1655.     sixteen_bits a C1("")@;
  1656. {
  1657. @<Expand module |a...@>@;
  1658. }
  1659.  
  1660. @* PRODUCING the OUTPUT.  The |get_output| routine above handles most of
  1661. the complexity of output generation, but there is one further consideration
  1662. that has a nontrivial effect on \.{TANGLE}'s algorithms.  Namely, we want
  1663. to make sure that the output has spaces and line breaks in the right places
  1664. (e.g., not in the middle of a string or a constant or an identifier, not at
  1665. a `\.{@@\&}' position where quantities are being joined together, and, if
  1666. in the C~language, certainly after a `\.=' because the C compiler thinks
  1667. `\.{=-}' is ambiguous).
  1668.  
  1669. The output process can be in one of following states (which are |enum|ed in
  1670. \.{typedefs.web}): 
  1671.  
  1672. \yskip\hang |NUM_OR_ID| means that the last item in the buffer is a number or
  1673. identifier, hence a blank space or line break must be inserted if the next
  1674. item is also a number or identifier.
  1675.  
  1676. \yskip\hang |UNBREAKABLE| means that the last item in the buffer was followed
  1677. by the \.{@@\&}~operation that inhibits spaces between it and the next item.
  1678.  
  1679. \yskip\hang |VERBATIM| means we're copying only character tokens, and
  1680. that they are to be output exactly as stored.  This is the case during
  1681. strings, verbatim constructions and numerical constants.
  1682.  
  1683. \yskip\hang |MISCELLANEOUS| means none of the above.
  1684.  
  1685. \yskip Furthermore, if the variable |protect| is positive, new-lines
  1686. are preceded by the value of the style-file field |protect|.
  1687.  
  1688. @<Global...@>=
  1689.  
  1690. EXTERN OUTPUT_STATE out_state; // Current status of partial output.
  1691. EXTERN boolean protect; // Current status of partial output.
  1692. EXTERN boolean copying_macros SET(NO); // Outputting outer macros?
  1693. EXTERN boolean in_cdir SET(NO); // Inside a compiler directive?
  1694.  
  1695. @ Here is a routine that is invoked when we want to output the current line.
  1696. During the output process, |cur_line| equals the number of the next line
  1697. to be output. This variable counts the total number of lines that have been
  1698. output. However, this is not useful for error messages when more than one
  1699. file are open. Thus, we introduce an array |outp_line| of current lines
  1700. that keeps track of what's going on in each individual language.  The
  1701. output line number for the current language is accessed by the macro
  1702. |OUTPUT_LINE|. 
  1703.  
  1704. @d flush_buffer() C_putc('\n')
  1705.  
  1706. @<Part 1@>=@[
  1707.  
  1708. SRTN flush0()
  1709. {
  1710. /* This routine might be called during phase~1, because error messages use
  1711. the output buffering routines.  However, we don't want to update
  1712. |cur_line|, which counts the input lines during phase~1. */
  1713. if(phase==1) return;
  1714.  
  1715. /* Give some feedback to the terminal by printing a dot every so often, and
  1716. the line number somewhat less often. */
  1717. if (cur_line % 100 == 0) 
  1718.     {
  1719.     if (cur_line % 500 == 0) {CLR_PRINTF(line_num,("%u",cur_line));}
  1720.     else putchar('.');
  1721.  
  1722.     UPDATE_TERMINAL; // Progress report.
  1723.     }
  1724.  
  1725. cur_line++;
  1726. OUTPUT_LINE++;
  1727. }
  1728.  
  1729. @* The BIG OUTPUT SWITCH.  Here then is the routine that does the
  1730. output:
  1731.  
  1732. @<Part 1@>=@[
  1733.  
  1734. SRTN phase2(VOID) 
  1735. {
  1736. phase = 2;
  1737.  
  1738. params = global_params;
  1739. frz_params();
  1740. set_output_file(global_language);
  1741.  
  1742. /* Get the FORTRAN output buffer ready. */
  1743. rst_out(NOT_CONTINUATION);
  1744.  
  1745. CLR_PRINTF(info,("\nWriting the %soutput file(s):",
  1746.     compare_outfiles ? "temporary " : "")); 
  1747. printf("  ");
  1748. UPDATE_TERMINAL;
  1749.  
  1750. cur_line = 1;
  1751.  
  1752. if (CAST(text_pointer,text_info)->text_link==0)
  1753.     { /* There was no program text. */
  1754.     CLR_PRINTF(warning, ("\n! No program text was specified.")); 
  1755.     mark_harmless;
  1756. @.No output was specified@>
  1757.       }
  1758. else 
  1759.     { /* There is program text. */
  1760.     @<Truncate identifiers@>;
  1761.  
  1762.     @<Initialize the output stacks@>;
  1763.     @<Output macro definitions@>;
  1764.  
  1765.     @<Initialize the output stacks@>;
  1766.  
  1767.     while(get_output())
  1768.         ; // Process each character of the output.
  1769.  
  1770.     flush_buffer();
  1771.  
  1772.     if(compare_outfiles)
  1773.         cmp_outfiles(); // Compare tangled output against old files.
  1774.  
  1775.     CLR_PRINTF(info,("\nDone."));
  1776.     }
  1777. }
  1778.  
  1779. @ The command line is written out at the very beginning of the output file 
  1780. as a meta-comment.
  1781. @<Part 1@>=@[
  1782. SRTN out_version FCN((msg))
  1783.     CONST outer_char *msg C1("")@;
  1784. {
  1785. outer_char HUGE *temp = GET_MEM("version:temp",N_MSGBUF,outer_char);
  1786. boolean in_string0 = in_string;
  1787. OUTPUT_STATE out_state0 = out_state;
  1788.  
  1789. SPRINTF(N_MSGBUF,temp,
  1790.     `"  FTANGLE v%s, created with %s on \"%s, %s at %s.\" %s\n",
  1791.     $VERSION,the_system,$DAY,$DATE,$TIME,local_banner`);
  1792. STRCAT(temp,cmd_ln_buf);
  1793. STRCAT(temp,msg); // Possible \.{@@o} continuation message.i
  1794. in_version = YES;
  1795. OUT_MSG(to_ASCII(temp),NULL);
  1796. FREE_MEM(temp,"version:temp",N_MSGBUF,outer_char);
  1797.  
  1798. in_version = NO;
  1799. in_string = in_string0;
  1800. out_state = out_state0;
  1801.  
  1802. if(line_info) 
  1803.     out_pos = 0;
  1804. else 
  1805.     {
  1806.     started_vcmnt = NO;
  1807.     rst_out(NOT_CONTINUATION);
  1808.     }
  1809. }
  1810.  
  1811. @ The version number is defined as the string |version| in \.{common.web}.
  1812. @<Define internal macros@>=
  1813.  
  1814. SAVE_MACRO("_VERSION $STRING($$VERSION)");
  1815. SAVE_MACRO("$VERSION $STRING($$VERSION)");
  1816.  
  1817. @ This internal function just puts the version number into the |macro_buf|.
  1818. @<Part 1@>=@[
  1819.  
  1820. SRTN i_version_ FCN((n,pargs))
  1821.     int n C0("")@;
  1822.     PARGS pargs C1("")@;
  1823. {
  1824. CHK_ARGS("$VERSION",0);
  1825.  
  1826. mcopy(version);
  1827. }
  1828.  
  1829. @ Here are the various time and date macros.
  1830.  
  1831. @m __DAY 0
  1832. @m __DATE 1
  1833. @m __TIME 2
  1834.  
  1835. @m STORE_TIME(macro,i)STORE_TIME0(#!macro $TM(i))
  1836. @m STORE_TIME0(s)SAVE_MACRO(#s)
  1837.  
  1838. @<Define internal macros@>=
  1839.  
  1840. STORE_TIME(_DAY,__DAY);
  1841. STORE_TIME(_DATE,__DATE);
  1842. STORE_TIME(_TIME,__TIME);
  1843.  
  1844. STORE_TIME($DAY,__DAY);
  1845. STORE_TIME($DATE,__DATE);
  1846. STORE_TIME($TIME,__TIME);
  1847.  
  1848. SAVE_MACRO("_TM(i)$STRING($$TM(i))");
  1849. SAVE_MACRO("$TM(i)$STRING($$TM(i))");
  1850.  
  1851. @ The date and time functions use the ANSII standard routines.
  1852. @<Part 1@>=@[
  1853.  
  1854. SRTN i_tm_ FCN((n,pargs))
  1855.     int n C0("")@;
  1856.     PARGS pargs C1("")@;
  1857. {
  1858. eight_bits HUGE *p;
  1859. struct tm *t;
  1860.  
  1861. CHK_ARGS("$TM",1);
  1862.  
  1863. p = pargs[0] + 1; // Should point to a single-digit constant.
  1864.  
  1865. if(*p++ != constant)
  1866.     {
  1867.     MACRO_ERR("Argument of $TM must be numerical constant",YES);
  1868.     return;
  1869.     }
  1870.  
  1871. t = the_localtime(); // Fill the |tm| structure and return a pointer.
  1872.  
  1873. switch(*p - @'0') 
  1874.     { /* Convert digit to integer and select routine. */
  1875.     case __DAY:
  1876.         mcopy(the_day(t));
  1877.         break;
  1878.  
  1879.     case __DATE:
  1880. /* The date needs to be protected because of the comma. */
  1881.         MCHECK(2,"the_cdate");
  1882.         *mp++ = @'`';
  1883.         mcopy(the_cdate(t));
  1884.         *mp++ = @'`';
  1885.         break;
  1886.  
  1887.     case __TIME:
  1888.         mcopy(the_time(t));
  1889.         break;
  1890.  
  1891.     default:
  1892.         MACRO_ERR("Invalid case in _tm_",YES);
  1893.         break;
  1894.     }
  1895. }
  1896.  
  1897. @ Here is a simple routine that copies an |outer_char| string into the
  1898. |macro_buf|, converting to |ASCII| as it does so.
  1899. @<Part 1@>=@[
  1900.  
  1901. SRTN mcopy FCN((s))
  1902.     CONST outer_char *s C1("")@;
  1903. {
  1904. int n = STRLEN(s);
  1905.  
  1906. MCHECK(n,"mcopy");
  1907. STRCPY(mp,x_to_ASCII(s));
  1908. mp += n;
  1909. }
  1910.  
  1911. @ First we go through the list of replacement texts and copy to the output
  1912. the macros that were defined by~\.{@@d}. These will be preceded by the
  1913. preprocesor \.{define} command appropriate for the language of that macro.
  1914.  
  1915. For the future, we really ought to have a mechanism that starts this list
  1916. after some position in the file that may not be the top.  That way, 
  1917.  
  1918. @<Output macro def...@>= 
  1919. @{
  1920. sixteen_bits a;
  1921. text_pointer cur_text;
  1922. boolean is_def;
  1923.  
  1924. @b
  1925. copying_macros = YES;
  1926.  
  1927. for (cur_text=text_info+1; cur_text<text_ptr; cur_text++)
  1928.   if (cur_text->text_link==macro) 
  1929.     { /* |cur_text| is the text for a macro */
  1930.         cur_byte=cur_text->tok_start;
  1931.         cur_end=(cur_text+1)->tok_start;
  1932.  
  1933.     is_WEB_macro = 
  1934.         BOOLEAN(!((is_def=BOOLEAN(cur_text->nargs==OUTER_MACRO)) ||
  1935.             cur_text->nargs==OUTER_UNMACRO)); /* Check special
  1936. marker set on input. */
  1937.  
  1938.     if(is_WEB_macro)
  1939.         {
  1940. #if(0)
  1941.         see_macro(cur_byte,cur_end) /* For debugging. */
  1942. #endif
  1943.         ;}
  1944.     else 
  1945.         @<Copy outer macro.@>@;
  1946.     }
  1947.  
  1948. copying_macros = NO;
  1949. }
  1950.  
  1951. @ Here we copy the non-WEB ``outer'' macros to the output. At the moment,
  1952. these always go to the very top of the output. This is not always
  1953. convenient, and someday we'll generalize.
  1954.  
  1955. @<Copy outer...@>=
  1956. {
  1957. LANGUAGE language0;
  1958. T_OUTER *po = &t_style.outer_start[lan_num(language)];
  1959. outer_char *outer_macro;
  1960.  
  1961. out_state = MISCELLANEOUS;
  1962.  
  1963. set_output_file((LANGUAGE)cur_text->Language); /* Set the language for this
  1964.                         outer macro.  */
  1965.  
  1966. protect = YES; // New-lines should be preceded by the protection character.
  1967.  
  1968. outer_macro = OC(is_def ? po->def : po->undef);
  1969. language0 = language;
  1970.  
  1971. C_sprintf(outer_macro,0);
  1972.  
  1973. stck_ptr = stack;
  1974. push_level(NULL,cur_byte,cur_end);
  1975.  
  1976. WHILE()
  1977.     @<Write one outer macro@>@;
  1978.  
  1979. set_output_file(language0);
  1980.  
  1981. protect = NO;
  1982. flush_buffer();
  1983. }
  1984.  
  1985. @
  1986. @<Write one outer macro@>=
  1987. {
  1988. if(DONE_LEVEL && !pop_level()) break;
  1989.  
  1990. a = *cur_byte++;
  1991.  
  1992. if (cur_byte==cur_end && a==@'\n') 
  1993.     continue;    // disregard a final new-line
  1994.  
  1995. if (TOKEN1(a)) 
  1996.     @<Send a single-byte token...@>@;
  1997. else 
  1998.     {
  1999.     a = IDENTIFIER(a,*cur_byte++);
  2000.  
  2001.         if (a<MODULE_NAME) 
  2002.         {
  2003.             cur_val=a; 
  2004.         OUT_CHAR(identifier);// Outer macro text will be expanded here.
  2005.             }
  2006.         else if (a!=MODULE_NUM) 
  2007.         {
  2008.         CONFUSION("copy outer","Macros defs have strange char");
  2009.         }
  2010.     else 
  2011.         {
  2012.             cur_mod = a - MODULE_NUM;
  2013.         cur_val = (long)cur_mod;
  2014.         OUT_CHAR(module_number); 
  2015.             }
  2016.     /* no other cases */
  2017.         }
  2018. }
  2019.  
  2020. @ If the switch |truncate_ids| is on, then we go through the list of
  2021. identifiers, strip off selected characters, and maybe truncate them.
  2022. (The code for truncating identifiers isn't completed in version~1.)
  2023.  
  2024. @<Truncate id...@>=
  2025. {
  2026. name_pointer np;
  2027.  
  2028. npmax = name_ptr - 1; // Used in output routine.
  2029.  
  2030. if(truncate_ids)
  2031.     {
  2032.     unsigned n = 0; // Counts number of truncations.
  2033.  
  2034.     printf("\nTruncating %u identifiers...",name_ptr - name_dir);
  2035.     
  2036.     for(np=name_dir+1; np<name_ptr; np++)
  2037.         n += trunc_id(np);
  2038.  
  2039.     printf("\n%u truncation(s) performed.",n);
  2040.     }
  2041.  
  2042. not_unique(); // Print non-unique identifiers.
  2043. }
  2044.  
  2045. @ Check for duplicate identifiers.
  2046.  
  2047. @d NEWLINE puts("")
  2048.  
  2049. @f TRUNC int
  2050. @f BP int
  2051.  
  2052. @<Part 1@>=@[
  2053.  
  2054. SRTN not_unique(VOID)
  2055. {
  2056. TRUNC HUGE *s,HUGE * HUGE *ss,HUGE * HUGE *ss0,HUGE * HUGE *ss1;
  2057. LANGUAGE Language;
  2058. int l;
  2059. size_t n; // Counts number of non-unique variables.
  2060. size_t num_max; // Maximum \# of roots for any duplicate.
  2061. BP HUGE * HUGE *bb0;
  2062. boolean found_dup = NO;
  2063.  
  2064. for(l=0; l<NUM_LANGUAGES; l++)
  2065.     {
  2066.     Language = lan_enum(l);
  2067.  
  2068. /* Count the number of duplicate variables. */
  2069.     n = 0;
  2070.  
  2071.     for(s=&sh; s->next; s=s->next)
  2072.         {
  2073.         if(!((boolean)s->Language & (boolean)Language)) continue;
  2074.  
  2075.         if(s->num[l] > 1)
  2076.             {
  2077.             char temp[10];
  2078.             unsigned len = tr_max[l];
  2079.  
  2080.             sprintf(temp,len ? "%u" : "*",len);
  2081.  
  2082.             if(n==0) 
  2083.                 {
  2084.                 printf("\n\n%c! Non-unique \
  2085. %s variables (filtered with {%s}, truncated to length %s):",
  2086.                 beep(1),languages[l],filter_char[l],temp);
  2087.                 found_dup = YES;
  2088.                 }
  2089.             n++;
  2090.             }
  2091.         }
  2092.  
  2093.     if(n == 0) continue;
  2094.  
  2095. /* Store the pointers to the duplicates in an array. */
  2096.     ss1 = ss0 = ss = GET_MEM("ss",n,TRUNC HUGE *);
  2097.     num_max = 0;
  2098.  
  2099.     for(s=&sh; s->next; s=s->next)
  2100.         {
  2101.         if(!((boolean)s->Language & (boolean)Language)) continue;
  2102.  
  2103.         if(s->num[l] > 1)
  2104.             {
  2105.             *ss++ = s;
  2106.             num_max = MAX(num_max,s->num[l]);
  2107.             }
  2108.         }
  2109.  
  2110. /* Sort the array. */
  2111.     QSORT(ss0,n,sizeof(TRUNC HUGE *),cmpr_trunc);
  2112.  
  2113. /* Print out the sorted array. */
  2114.     bb0 = GET_MEM("bb",num_max,BP HUGE *);
  2115.  
  2116.     while(ss1 < ss)
  2117.         see_dup(*ss1++,Language,bb0);
  2118.  
  2119.     FREE_MEM(ss0,"ss",n,TRUNC HUGE *);
  2120.     FREE_MEM(bb0,"bb",num_max,BP HUGE *);
  2121.     }
  2122.  
  2123. if(found_dup)
  2124.     NEWLINE;
  2125. }
  2126.  
  2127. SRTN see_dup FCN((s,Language,bb0))
  2128.     CONST TRUNC HUGE *s C0("")@;
  2129.     LANGUAGE Language C0("")@;
  2130.     BP HUGE *HUGE *bb0 C1("")@;
  2131. {
  2132. BP HUGE *b, HUGE * HUGE *bb, HUGE * HUGE *bb1;
  2133. int n;
  2134.  
  2135. NEWLINE;
  2136. printf(" "); 
  2137. n = see(s->id,s->id_end); // The truncated id.
  2138.  
  2139. /* Space it out so it looks nicely lined up. */
  2140. for(n = tr_max[lan_num(Language)] + 1 - n; n > 0; n--) printf(" ");
  2141. printf("<=");
  2142.  
  2143. /* Print all back references to original variables. */
  2144. for(b=s->first,bb=bb0; b != NULL; b=b->next)
  2145.     {
  2146.     if(!((boolean)b->Language & (boolean)Language)) continue;
  2147.  
  2148.     *bb++ = b;
  2149.     }
  2150.  
  2151. QSORT(bb0,bb-bb0,sizeof(BP HUGE *),cmpr_bp);
  2152.  
  2153. for(bb1=bb0; bb1<bb; bb1++)
  2154.     {
  2155.     printf(" ");    
  2156.     see((*bb1)->byte_start,(*bb1)->byte_end);
  2157.     }
  2158. }
  2159.  
  2160. int see FCN((c0,c1))
  2161.     CONST ASCII HUGE *c0 C0("Beginning.")@;
  2162.     CONST ASCII HUGE *c1 C1("end.")@;
  2163. {
  2164. int n = c1 - c0;
  2165.  
  2166. while(c0 < c1) printf("%c",XCHR(*c0++));
  2167.  
  2168. return n; // Length of identifier.
  2169. }
  2170.  
  2171. @
  2172. @<Part 1@>=@[
  2173.  
  2174. int cmpr_trunc FCN((t0,t1))
  2175.     TRUNC HUGE **t0 C0("")@;
  2176.     TRUNC HUGE **t1 C1("")@;
  2177. {
  2178. switch(web_strcmp((*t0)->id,(*t0)->id_end,(*t1)->id,(*t1)->id_end))
  2179.     {
  2180.    case EQUAL:
  2181.     return 0;
  2182.  
  2183.    case LESS:
  2184.    case PREFIX:
  2185.     return -1;
  2186.  
  2187.    case GREATER:
  2188.    case EXTENSION:
  2189.     return 1;
  2190.     }
  2191.  
  2192. return 0;
  2193. }
  2194.  
  2195. int cmpr_bp FCN((bb0,bb1))
  2196.     BP HUGE **bb0 C0("")@;
  2197.     BP HUGE **bb1 C1("")@;
  2198. {
  2199. switch(web_strcmp((*bb0)->byte_start,(*bb0)->byte_end,
  2200.         (*bb1)->byte_start,(*bb1)->byte_end))
  2201.     {
  2202.    case EQUAL:
  2203.     return 0;
  2204.  
  2205.    case LESS:
  2206.    case PREFIX:
  2207.     return -1;
  2208.  
  2209.    case GREATER:
  2210.    case EXTENSION:
  2211.     return 1;
  2212.     }
  2213.  
  2214. return 0;
  2215. }
  2216.  
  2217. @i trunc.hweb
  2218.  
  2219. @ Define the first truncation structure.
  2220. @<Glob...@>=
  2221.  
  2222. EXTERN TRUNC sh;
  2223.  
  2224. @ Attach a back-pointer structure to a |TRUNC| structure.
  2225. @<Part 1@>=@[
  2226.  
  2227. BP HUGE *b_link FCN((s,Language,p0,p1))
  2228.     TRUNC HUGE *s C0("")@;
  2229.     LANGUAGE Language C0("")@;
  2230.     CONST ASCII HUGE *p0 C0("")@;
  2231.     CONST ASCII HUGE *p1 C1("")@;
  2232. {
  2233. BP HUGE *bp;
  2234.  
  2235. bp = GET_MEM("bp",1,BP); /* Get a back-pointer structure. */
  2236.  
  2237. bp->c = BP_MARKER;
  2238.  
  2239. /* Remember language of original variable. */
  2240. bp->Language = Language;
  2241.  
  2242. /* Record start and end of the original name. */
  2243. bp->byte_start = p0;
  2244. bp->byte_end = p1;
  2245.  
  2246. /* Link back to original |TRUNC| structure. */
  2247. bp->Root = s;
  2248. s->Language |= (boolean)Language;
  2249. s->num[lan_num(Language)]++; /* Count hits for this language. */
  2250.  
  2251. return bp;
  2252. }
  2253.  
  2254. @
  2255. Attach a |TRUNC| structure to the chain of truncated ids.
  2256. @<Part 1@>=@[
  2257.  
  2258. TRUNC HUGE *s_link FCN((s,id,len))
  2259.     TRUNC HUGE *s C0("Points to the current structure, to be \
  2260. filled with info.")@;
  2261.     CONST ASCII HUGE *id C0("Truncated identifier.")@;
  2262.     unsigned short len C1("Length of truncated identifier.")@;
  2263. {
  2264. /* Fill this structure with truncated variable name. */
  2265. s->id = GET_MEM("s->id",len,ASCII); // Space for name.
  2266. STRNCPY(s->id,id,len); // Copy over name.
  2267. s->id_end = s->id + len; // End of name.
  2268.  
  2269. /* Attach another (uninitialized) structure. */
  2270. s->next = GET_MEM("s->next",1,TRUNC);
  2271.  
  2272. return s; 
  2273. }
  2274.  
  2275. @ Search for identifier in table.
  2276. @<Part 1@>=@[
  2277.  
  2278. name_pointer id0_lookup FCN((start,end,l))
  2279.     CONST ASCII HUGE *start C0("Start of name.")@;
  2280.     CONST ASCII HUGE *end C0("end of name.")@;
  2281.     LANGUAGE l C1("")@;
  2282. {
  2283. name_pointer np;
  2284. CONST ASCII HUGE *p0, HUGE *p1;
  2285.  
  2286. for(np=name_dir+1; np<name_ptr; np++)
  2287.     {
  2288.     if(!(np->Language & (boolean)l) ||
  2289.         np->equiv != NULL || *(p0=np->byte_start) == BP_MARKER)
  2290.             continue; 
  2291.  
  2292.     PROPER_END(p1);
  2293.  
  2294.     if(web_strcmp(p0,p1,start,end) == EQUAL) 
  2295.         return np;
  2296.     }
  2297.  
  2298. return NULL;
  2299. }
  2300.  
  2301. @ Test if a character~|c| is valid for an identifier in the $l$th~language.
  2302. @<Unused@>=
  2303.  
  2304. boolean valid_char FCN((c,l))
  2305.     ASCII c C0("Character to be tested.")@;
  2306.     int l C1("Language index.")@;
  2307. {
  2308. return BOOLEAN(STRCHR(filter_char[l],(int)XCHR(c)) == NULL);
  2309. /* If the character isn't a filter character, we return |YES|. */
  2310. }
  2311.  
  2312. @ Truncate an identifier.
  2313. @<Part 1@>=@[
  2314.  
  2315. unsigned trunc_id FCN((np0))
  2316.     CONST name_pointer np0 C1("Points to current id structure.")@;
  2317. {
  2318. CONST ASCII HUGE *p, HUGE *p0, HUGE *p1; // For original identifier.
  2319. ASCII temp[N_IDBUF];
  2320. ASCII HUGE *t; // For truncated identifier.
  2321. unsigned short n; // Length of truncated identifier.
  2322. TRUNC HUGE *s;
  2323. name_pointer np;
  2324. unsigned short nmax; // Truncate to this length.
  2325. LANGUAGE Language;
  2326. int l;
  2327. unsigned count = 0;
  2328.  
  2329. if(np0->Language == (boolean)NO_LANGUAGE || np0->equiv != NULL) 
  2330.     return 0;
  2331.  
  2332. for(l=0; l<NUM_LANGUAGES; l++)
  2333.  {
  2334. Language = lan_enum(l);
  2335. np = np0;
  2336.  
  2337. /* Don't bother with it if there's no truncation specified for this
  2338. language, if it's not in use for this language, if it's a reserved word,
  2339. intrinsic word, or keyword, or if it's a \WEB\ macro. */
  2340.  if( (nmax = tr_max[l]) == 0 || !(np->Language & (boolean)Language)
  2341.     || (np->reserved_word & (boolean)Language)
  2342.     || (np->intrinsic_word & (boolean)Language)
  2343.     || (np->keyword & (boolean)Language)
  2344.     || (np->macro_type != NOT_DEFINED) )
  2345.         continue; 
  2346.  
  2347. /* The original name. */
  2348. p0 = np->byte_start;
  2349.  
  2350. if(*p0 == BP_MARKER) 
  2351.     continue; /* NEED MORE WORK HERE (variable already deflected). */
  2352.  
  2353. PROPER_END(p1);
  2354.  
  2355. /* Filter. */
  2356. for(p=p0,t=temp,n=0; p<p1 && n<nmax; p++)
  2357.     if(STRCHR(filter_char[l],(int)XCHR(*p)) == NULL)
  2358.         {
  2359.         n++;
  2360.         *t++ = *p;
  2361.         }
  2362.  
  2363. n = t - temp; // Length of truncated identifier.
  2364.  
  2365. if(p1-p0 == (long)n) 
  2366.     continue; // Not truncated; nothing to do.
  2367.  
  2368. count++; // Count number of truncations for this identifier.
  2369.  
  2370. /* Is the truncated name already in the list? */
  2371. for(s= &sh; s->next != NULL; s=s->next)
  2372.     if(s->id_end - s->id == (long)n &&
  2373.             web_strcmp(s->id,s->id_end,temp,t) == EQUAL)
  2374.         {
  2375.     another_bp:
  2376.         s->last = s->last->next = b_link(s,Language,p0,p1); 
  2377. /* Remember the original variable by attaching another back reference. */
  2378.         np->byte_start = (ASCII *)s->last; // Deflect original ptr.
  2379.         goto next_language;
  2380.         }
  2381.  
  2382. /* Add a new name to the list. */
  2383. s = s_link(s,temp,n);
  2384. s->first = s->last = b_link(s,Language,p0,p1); // Attach first back reference.
  2385. np->byte_start = (ASCII *)s->first; // Deflect original ptr.
  2386.  
  2387. /* If the truncated name was in the original list, not previously truncated
  2388. from something else, put the original name into the truncated list. */
  2389. if( (np = id0_lookup(temp,t,(LANGUAGE)np->Language)) != NULL) 
  2390.     {
  2391.     p0 = np->byte_start; PROPER_END(p1);
  2392.     goto another_bp;
  2393.     }
  2394.  
  2395. next_language:;
  2396.  }
  2397.  
  2398. return count;
  2399. }
  2400.  
  2401. @ Here are some flags used in the output routine |out_char|.
  2402. @<Glob...@>=
  2403.  
  2404. EXTERN boolean mac_protected SET(NO); /* Are we between left quotes, so macros
  2405.                 shouldn't be expanded? */
  2406. EXTERN boolean send_rp SET(NO); /* Takes on a value only for |language ==
  2407.     RATFOR || language==FORTRAN|, when it's used to enclose the rhs of
  2408.     an operator like \.{*=}. */ 
  2409.  
  2410. EXTERN boolean in_version SET(NO); // For the initial header of output file.
  2411. EXTERN T_META *pmeta;
  2412.  
  2413. @ This fragment finishes off a~\.{*=} or similar operator by enclosing the
  2414. right-hand side expressions in parentheses.
  2415. @<Maybe send a right parenthesis@>=
  2416.  
  2417. if(send_rp)
  2418.     {
  2419.     C_putc(')');
  2420.     send_rp = NO; // Clear the flag.
  2421.     }
  2422.  
  2423. @ A many-way switch, |out_char()|, is used to send the output. Because of
  2424. macro expansion, this routine needs to be  recursive. It performs a variety
  2425. of actions, including inserting spaces at desired places (such as after
  2426. equals and between identifiers), translating internal codes to their
  2427. visible representations such as~\.{++}, etc.
  2428.  
  2429. @<Part 2@>=@[
  2430.  
  2431. eight_bits out_char FCN((cur_char))
  2432.     eight_bits cur_char C1("Token to control or be sent to the output.")@;
  2433. {
  2434. switch(cur_char) 
  2435.     {
  2436.    case ignore: 
  2437.     if(R77_or_F && started_vcmnt) C_putc(cur_char);
  2438.     return @' '; /* KLUDGE  to prevent |get_output| from being
  2439. terminated prematurely. */
  2440.  
  2441. /* In nuweb mode, tab is mapped to bell on input, and back again here. */
  2442.    case bell:
  2443.     return out_dflt(tab_mark);
  2444. @%   case bell: break; /* Bells go to the tty, but not the output file. */
  2445.  
  2446.    case @',':
  2447.     out_dflt(cur_char);
  2448.     @<Mark split position@>@;
  2449.     break;
  2450.  
  2451.    case interior_semi:
  2452.     if(!(Fortran88||in_string)) cur_char = @';'; 
  2453.         // Fall through to regular semicolon.
  2454.  
  2455.    case @';':
  2456.     @<Maybe send a right paren...@>;
  2457.     return out_dflt(cur_char);
  2458.  
  2459.    case cdir:
  2460.     in_cdir = BOOLEAN(!in_cdir);
  2461.     if(FORTRAN_LIKE(language))
  2462.         {
  2463.         in_string = NO;
  2464.         flush_buffer();
  2465.         in_string = YES;
  2466.         }
  2467.     break;
  2468.  
  2469.    case @'\n': 
  2470.     if((copying_macros || !nuweb_mode)
  2471.             && (protect || out_state==VERBATIM) ) 
  2472.         {
  2473. /* Outer macros are absorbed with no explicit backslash at end of line.
  2474. Furthermore, spaces are stripped from the start of the next line. 
  2475. Therefore, we will think of the end of line as a space.  Contrast this with
  2476. explicit \.{\#define}'s continued with a backslash, which just abuts the
  2477. last character of the line with the first character of the next one. */
  2478.         if(copying_macros && protect)
  2479.             C_putc(' ');
  2480.  
  2481.         out_str(t_style.protect_chars[lan_num(language)]); 
  2482.             /* Backslash at end of line. */
  2483.         }
  2484.     @<Maybe send a right paren...@>;
  2485.     flush_buffer(); 
  2486.     if (out_state!=VERBATIM) 
  2487.         out_state = MISCELLANEOUS; 
  2488.     break;
  2489.  
  2490.    @t\4@>@<Case of an identifier@>;
  2491.    @t\4@>@<Case of a module number@>;
  2492.    @t\4@>@<Cases like \.{!=}@>;
  2493.    @t\4@>@<Cases like \.{+=}@>;
  2494.  
  2495.    case @'=': 
  2496.     C_putc('='); 
  2497.     if (out_state!=VERBATIM) 
  2498.         {
  2499.         if(C_LIKE(language) && !nuweb_mode) 
  2500.             C_putc(' '); // Space after equals.
  2501.  
  2502.         out_state = MISCELLANEOUS;
  2503.         }
  2504.     @<Mark split position@>@;
  2505.     break;
  2506.  
  2507.    case join: out_state = UNBREAKABLE; break; 
  2508.  
  2509.    case constant: 
  2510.     if (out_state==VERBATIM) 
  2511.         out_state= in_format ? MISCELLANEOUS : NUM_OR_ID; 
  2512.             // End of constant.
  2513.     else
  2514.         { /* Beginning of constant. */
  2515.         @<Mark split...@>@;
  2516.  
  2517.             if(out_state==NUM_OR_ID && !nuweb_mode) 
  2518.             C_putc(' '); 
  2519.  
  2520.         out_state = VERBATIM; 
  2521.         }
  2522.  
  2523.     in_constant = BOOLEAN(!in_constant);
  2524.     break;
  2525.  
  2526.    case stringg: 
  2527.     if(in_string) 
  2528.         out_state = MISCELLANEOUS; // End of string.
  2529.         else 
  2530.         { /* Begining of string. */
  2531.         @<Mark split...@>@;
  2532.  
  2533.         if(out_state == NUM_OR_ID && !nuweb_mode) 
  2534.             C_putc(' '); /* Strings after
  2535. identifiers can happen in macro definitions. */
  2536.  
  2537.         out_state = VERBATIM;
  2538.         }
  2539.  
  2540.     in_string = BOOLEAN(!in_string);
  2541.     break;
  2542.  
  2543.    case begin_meta: 
  2544. /* If there are two |begin_meta|s in a row, the second one means to turn
  2545. off the |xpn_Ratfor| flag, which among other things is used to control the
  2546. spacing after the comment character in \Fortran\ output. */
  2547.     pmeta = &t_style.meta[lan_num(language)];
  2548.  
  2549.     switch(language)
  2550.         {
  2551.         outer_char *t;
  2552.  
  2553.        case C:
  2554.        case C_PLUS_PLUS:
  2555.        case LITERAL:
  2556.        case TEX:
  2557.         if(meta_mode) break;
  2558.  
  2559.         if(!nuweb_mode)
  2560.             {
  2561.             if(in_string && !in_version)
  2562.                 OUT_STR(t=pmeta->msg.top);
  2563.             else 
  2564.                 OUT_OP(t=pmeta->hdr.top);
  2565.  
  2566.             if(*t) OUT_STR("\n"); // Necessary????
  2567.             }
  2568.         meta_mode = YES;
  2569.         break;
  2570.  
  2571.        case RATFOR:
  2572.        case RATFOR_90:
  2573.        case FORTRAN:
  2574.        case FORTRAN_90:
  2575.         if(meta_mode) 
  2576.             xpn_Ratfor = NO;
  2577.          C_putc(cur_char);
  2578.         out_state = MISCELLANEOUS;
  2579.         break;
  2580.    
  2581.        default: 
  2582.         CONFUSION("out_char:begin_meta","Language not defined");
  2583.         }
  2584.     break;
  2585.  
  2586.    case end_meta:
  2587.     meta_mode = NO;
  2588.  
  2589.     switch(language)
  2590.         {
  2591.         outer_char *t;
  2592.  
  2593.        case C:
  2594.        case C_PLUS_PLUS:
  2595.        case LITERAL:
  2596.        case TEX:
  2597.         if(meta_mode) break;
  2598.  
  2599.         if(!nuweb_mode)
  2600.             {
  2601.             if(in_string && !in_version)
  2602.                 OUT_OP(t=pmeta->msg.bottom);
  2603.             else 
  2604.                 OUT_OP(t=pmeta->hdr.bottom);
  2605.  
  2606.             if(*t) OUT_OP("\n"); // Necessary????
  2607.             }
  2608.         break;
  2609.  
  2610.        case RATFOR:
  2611.        case RATFOR_90:
  2612.        case FORTRAN:
  2613.        case FORTRAN_90:
  2614.         xpn_Ratfor = YES;
  2615.         C_putc(cur_char);
  2616.         out_state = MISCELLANEOUS;
  2617.         break;
  2618.     
  2619.        default: 
  2620.         CONFUSION("out_char:end_meta","Language not defined");
  2621.         }
  2622.  
  2623.     break;
  2624.         
  2625.    case @'{':
  2626.     if(R77 && !in_string) 
  2627.         @<Copy function body@>@;
  2628.     else 
  2629.         {
  2630.         @<Mark split...@>@;
  2631.         return out_dflt(cur_char);
  2632.         }
  2633.     break;
  2634.  
  2635. /* The following doesn't work right when there's no |program| statement. */
  2636.    case @'}':
  2637.     {
  2638.     if(R77 && !in_string && brace_level==0) 
  2639.         RAT_ERROR(WARNING,"Spurious '}' ignored, \
  2640. or missing program, module, subroutine, or function statement",0); 
  2641.     else 
  2642.         {
  2643.         out_dflt(cur_char);
  2644.         @<Mark split...@>@;
  2645.         }
  2646.     }
  2647.  
  2648.     break;
  2649.  
  2650.    case @'[':
  2651.     out_bracket(cur_char,@'(');
  2652.     break;
  2653.  
  2654.    case @']':
  2655.     out_bracket(cur_char,@')');
  2656.     break;
  2657.  
  2658.  
  2659.    case @'`':
  2660.     if(!(in_string || language==LITERAL))
  2661.         {
  2662.         mac_protected = BOOLEAN(!mac_protected);
  2663.         break;
  2664.         }
  2665.     else 
  2666.         return out_dflt(cur_char);
  2667.  
  2668.    case @'&':
  2669.     if(C_LIKE(language) && out_state != VERBATIM
  2670.             && *(pC_buffer-1) == '&' && !nuweb_mode) 
  2671.         C_putc(' '); // Handle the situation |x & &y|.
  2672.     @<Mark split...@>@;
  2673.     return out_dflt(cur_char);
  2674.  
  2675.    case @'\\': 
  2676.     if(R66) 
  2677.         cur_char = @'$'; /* Change octal constant to \Ratfor's
  2678. argument token. This is kludgy and obsolete. */ 
  2679.  
  2680.    default: 
  2681.     return out_dflt(cur_char);
  2682.     }
  2683.  
  2684. return cur_char;
  2685. }
  2686.  
  2687. @
  2688. @<Part 2@>=@[
  2689.  
  2690. eight_bits out_bracket FCN((cur_char,new_char))
  2691.     eight_bits cur_char C0("")@;
  2692.     eight_bits new_char C1("")@;
  2693. {
  2694. if(out_state != VERBATIM && FORTRAN_LIKE(language) && translate_brackets) 
  2695.     cur_char = new_char;
  2696. return out_dflt(cur_char);
  2697. }
  2698.  
  2699. @
  2700. @<Mark split...@>=
  2701. {
  2702. #if FANCY_SPLIT
  2703.     if(C_LIKE(language) && out_state!=VERBATIM)
  2704.         split_pos = pC_buffer;
  2705. #endif /* |FANCY_SPLIT| */
  2706. }
  2707.  
  2708. @ In \Ratfor-77 mode, when we sense an opening brace, we copy everything
  2709. between matched braces.
  2710. @<Copy function body@>=
  2711. {
  2712. cp_fcn_body(); /* See \.{rat77.web}. */
  2713. cur_char = 01;
  2714. }
  2715.  
  2716. @ Send a single character to the output.
  2717. @<Part 2@>=@[
  2718.  
  2719. eight_bits out_dflt FCN((c))
  2720.     eight_bits c C1("")@;
  2721. {
  2722. C_putc(XCHR(c)); 
  2723.  
  2724. if (out_state != VERBATIM) 
  2725.     out_state = MISCELLANEOUS;
  2726.  
  2727. return c;
  2728. }
  2729.  
  2730. @
  2731. @<Cases for appending a lan...@>=
  2732.  
  2733. case C: opn_output_file(C); @+ break;
  2734. case C_PLUS_PLUS: opn_output_file(C_PLUS_PLUS); @+ break;
  2735. case RATFOR: 
  2736.     if(!RAT_OK("(send_single)")) 
  2737.         CONFUSION("output default","Ratfor command during output");
  2738.     opn_output_file(RATFOR); 
  2739.     break;
  2740. case RATFOR_90:
  2741.     if(!RAT_OK("(send_single)")) 
  2742.         CONFUSION("output default","Ratfor command during output");
  2743.     opn_output_file(RATFOR_90); 
  2744.     break;
  2745. case FORTRAN: opn_output_file(FORTRAN); @+ break;
  2746. case FORTRAN_90: opn_output_file(FORTRAN_90); @+ break;
  2747. case TEX: opn_output_file(TEX); @+ break;
  2748. case LITERAL: opn_output_file(LITERAL); @+ break@;
  2749.  
  2750. @ When we switch languages, we must select the appropriate output file, and
  2751. set up any relevant parameters.
  2752.  
  2753. @<Part 2@>=@[
  2754.  
  2755. LANGUAGE set_output_file FCN((language0))
  2756.     LANGUAGE language0 C1("")@;
  2757. {
  2758. language = language0; // Set the input language.
  2759. out_file = params.OUT_FILE; // Output of \.{TANGLE}.
  2760. ini0_language(); // Set up parameters.
  2761.  
  2762. return language; // Return the input language.
  2763. }
  2764.  
  2765. @ The |set_output_file| routine doesn't open a file.  The following
  2766. function does, in response to a |begin_language| seen by |send_single|
  2767. during the output phase.
  2768.  
  2769. @<Part 2@>=@[
  2770.  
  2771. LANGUAGE opn_output_file FCN((language0))
  2772.     LANGUAGE language0 C1("")@;
  2773. {
  2774. set_output_file(language0);
  2775. flush_buffer();
  2776. open_out(OC(""), LOCAL_SCOPE);
  2777.  
  2778. return language;
  2779. }
  2780.  
  2781. @ Output files are opened only when necessary, during phase~2.
  2782.  
  2783. @d GLOBAL_SCOPE YES
  2784. @d LOCAL_SCOPE NO
  2785.  
  2786. @d CHECK_OPEN if(!out_file) open_out(OC(""), GLOBAL_SCOPE)
  2787.  
  2788. @<Part 1@>=@[
  2789.  
  2790. SRTN open_out FCN((msg,global_scope))
  2791.     CONST outer_char *msg C0("")@;
  2792.     boolean global_scope C1("")@;
  2793. {
  2794. boolean is_stdout = BOOLEAN(STRCMP(params.OUTPUT_FILE_NAME,"stdout") == 0);
  2795. boolean already_opened = NO;
  2796.  
  2797. if(is_stdout) 
  2798.     out_file = params.OUT_FILE = stdout;
  2799. else 
  2800.     {
  2801.     already_opened = was_opened(params.OUTPUT_FILE_NAME, global_scope,
  2802.                     NULL, &out_file);
  2803.  
  2804.     params.OUT_FILE = out_file; // Local output file.
  2805.  
  2806. /* Write header info to the newly opened file. (We don't write it for
  2807. |stdout|, because it clutters up the screen.) */ 
  2808.     if(top_version && !(already_opened || compare_outfiles)) 
  2809.         out_version(msg); 
  2810.     }
  2811.  
  2812. /* The first time a file is opened for a particular language, its |FILE|
  2813. pointer must be made global so it can be restored at the beginning of each
  2814. module.  (The name was already made global in |common_init|.) */ 
  2815. if(global_scope) 
  2816.     cur_global_params.OUT_FILE = global_params.OUT_FILE = out_file;
  2817.  
  2818. /* The first time a file is opened, write its name to the screen. */
  2819. if(!already_opened)
  2820.  {
  2821.  CLR_PRINTF(out_file,("(%s)%s",params.OUTPUT_FILE_NAME,is_stdout ? "\n" : ""));
  2822.  UPDATE_TERMINAL;
  2823.  }
  2824. }
  2825.  
  2826. @ Information about previously opened files is stored in a dynamically
  2827. allocated list.
  2828.  
  2829. @<Glob...@>=
  2830.  
  2831. EXTERN OPEN_FILE HUGE *open_file, HUGE *open_file_end, HUGE *last_file;
  2832. EXTERN BUF_SIZE num_files; // Allocated length of |open_file|.
  2833.  
  2834. @ A list of |open_files| needs to be in place before the command line is
  2835. scanned.  The initial allocation gets the default value.
  2836.  
  2837. @<Allocate initial tables@>=
  2838. {
  2839. ALLOC(OPEN_FILE,open_file,ABBREV(num_files),num_files,0);
  2840. last_file = open_file;
  2841. open_file_end = open_file + num_files;
  2842. }
  2843.  
  2844. @ After the command line has been scanned, we may want to reallocate this
  2845. table. 
  2846.  
  2847. @<Allocate dyn...@>=
  2848. {
  2849. BUF_SIZE cur_num = last_file - open_file; // Current size of list.
  2850.  
  2851. /* Obtain the new allocation size. */
  2852. alloc((outer_char *)ABBREV(num_files),(BUF_SIZE HUGE *)&num_files,
  2853.     sizeof(*open_file),-1);
  2854.  
  2855. /* Reallocate and reset parameters. */
  2856. open_file = (OPEN_FILE *)REALLOC(open_file,num_files*sizeof(OPEN_FILE));
  2857. last_file = open_file + cur_num;
  2858. open_file_end = open_file + num_files;
  2859. }
  2860.  
  2861. @ Here we check if the output file about to be opened has already been
  2862. previously opened.  If not, we put it into the list.
  2863.  
  2864. The variable |pname| is used as a flag.  If it's |NULL|, the file is opened
  2865. if necessary.  Otherwise, a pointer to the previously allocated storage
  2866. area for the name is returned.
  2867.  
  2868. @<Part 1@>=@[
  2869.  
  2870. boolean was_opened FCN((file_name,global_scope,pname,pfile_ptr))
  2871.     CONST outer_char HUGE *file_name C0("")@;
  2872.     boolean global_scope C0("")@;
  2873.     outer_char HUGE * HUGE *pname C0("")@;
  2874.     FILE **pfile_ptr C1("")@;
  2875. {
  2876. OPEN_FILE HUGE *f;
  2877.  
  2878. if(!*file_name)
  2879.     { /* Take care of special cases called by |xpn_name|. */
  2880.     *pname = (outer_char HUGE *)"";
  2881.     *pfile_ptr = NULL;
  2882.     return NO;
  2883.     }
  2884.  
  2885. /* Is file already in the list of previously opened? */
  2886. for(f=open_file; f<last_file; f++)
  2887.     if(STRCMP(f->name,file_name)==0) 
  2888.         {
  2889.         if(pname) 
  2890.             { /* Just return (to |new_fname|) some information. */
  2891.             *pname = f->name;
  2892.             *pfile_ptr = f->ptr;
  2893.             return f->previously_opened;
  2894.             }
  2895.         else 
  2896.             goto open_it;
  2897.         }
  2898.  
  2899. @<Add a new file to the list@>@;
  2900.  
  2901. if(pname) 
  2902.     { /* File wasn't previously opened, and has now been added to list
  2903. of file names. */
  2904.     *pname = f->name;
  2905.     f->ptr = NULL;
  2906.     f->previously_opened = NO;
  2907.     f->global_scope = global_scope;
  2908.     }
  2909. else
  2910.     @<Possibly open the file@>@;
  2911.  
  2912. *pfile_ptr = f->ptr;
  2913. return f->previously_opened;
  2914. }
  2915.  
  2916. @
  2917. @<Add a new file...@>=
  2918. {
  2919. /* File not in list; is there room for more? */
  2920. if(last_file==open_file_end) 
  2921.     {
  2922.     OVERFLW("previously opened files",ABBREV(num_files));
  2923.     }
  2924.  
  2925. last_file->name = GET_MEM("last_file",STRLEN(file_name)+1,outer_char);
  2926. STRCPY(last_file->name,file_name);
  2927. last_file++;
  2928. }
  2929.  
  2930. @ |f|~is now pointing to the proper entry in the list.  We're ready to open
  2931. the file.  If the file is already open, its file pointer is non-null, so we
  2932. do nothing except set the |previously_opened| flag.  (This might have
  2933. already been turned on when a file with local scope was closed.)  If it was
  2934. previously opened, but is now closed (|f->ptr == NULL|), we open it into
  2935. append mode.  Otherwise, it has never been opened and we must create a new
  2936. file name and open it into write mode.
  2937.  
  2938. @<Possibly open the file@>=
  2939. {
  2940. open_it:
  2941.   f->previously_opened = BOOLEAN(f->previously_opened || (f->ptr != NULL));
  2942.  
  2943. if(f->previously_opened)
  2944.     { /* It might have been once opened, but then closed. */
  2945.     if(f->ptr == NULL)
  2946.         f->ptr = FOPEN(compare_outfiles ? f->tmp_name : f->name, "a");
  2947.     }
  2948. else    
  2949.     { /* File wasn't ever opened. */
  2950.     if(compare_outfiles)
  2951.         @<Actually write into a temporary file@>@;
  2952.     else
  2953.         f->ptr = FOPEN(f->name, "w");
  2954.  
  2955.     if(!(f->ptr))
  2956.         { /* Should upgrade this message. */
  2957.         FATAL("\n!! Can't open output file ", file_name);
  2958.         }
  2959.     }
  2960. }
  2961.  
  2962. @ We do the following when |compare_outfiles == YES|.
  2963.  
  2964. @<Actually write into a temp...@>=
  2965. {
  2966. char *buffer;
  2967. IN_COMMON outer_char wbprefix[MAX_FILE_NAME_LENGTH];
  2968.  
  2969. #if(HAVE_TEMPNAM)
  2970.     extern char *tempnam();
  2971.  
  2972.     if(!*wbprefix) STRCPY(wbprefix,"./");
  2973.     buffer = tempnam(wbprefix, "FTMP"); 
  2974.     // Non-|ANSI|, but more control over directory.
  2975. #else
  2976.     buffer = tmpnam(NULL); // |ANSI| routine.
  2977. #endif
  2978.  
  2979. f->tmp_name = GET_MEM("f->tmp_name",STRLEN(buffer)+1,outer_char); 
  2980.  
  2981. STRCPY(f->tmp_name, buffer);
  2982.  
  2983. f->ptr = FOPEN(f->tmp_name, "w");
  2984. }
  2985.  
  2986. @ Here we  close a file in response to an \.{@@O} command.
  2987. @<Part 1@>=@[
  2988.  
  2989. SRTN close_out FCN((fp))
  2990.     FILE *fp C1("")@;
  2991. {
  2992. OPEN_FILE *f;
  2993.  
  2994. for(f=open_file; f<last_file; f++)
  2995.     if(f->ptr == fp)
  2996.         {
  2997.         close0(f);
  2998.         return;
  2999.         }
  3000.  
  3001. CONFUSION("close_out", "Allegedly open file isn't in list");
  3002. }
  3003.  
  3004. @ Files with local scope are closed at the end of a section.
  3005. @<Part 1@>=@[
  3006.  
  3007. SRTN cls_local(VOID)
  3008. {
  3009. OPEN_FILE *f;
  3010.  
  3011. for(f=open_file; f<last_file; f++)
  3012.     if(f->ptr && !f->global_scope)
  3013.         close0(f);
  3014. }
  3015.  
  3016. @ Here's a nucleus for closing output files.
  3017. @<Part 1@>=@[
  3018. SRTN close0 FCN((f))
  3019.     OPEN_FILE *f C1("")@;
  3020. {
  3021. fclose(f->ptr);
  3022. f->ptr = NULL;
  3023. f->previously_opened = YES;
  3024. }
  3025.  
  3026. @ Here we go through the list of all potentially open files.  If it's open,
  3027. we compare the temporary file that was just written with what already
  3028. exists on disk.  If they're the same, the old one is kept; otherwise, the
  3029. temporary file is made the new one.
  3030.  
  3031. @<Part 1@>=@[
  3032. SRTN cmp_outfiles(VOID)
  3033. {
  3034. OPEN_FILE *f;
  3035. boolean renamed = NO;
  3036.  
  3037. printf("\nRenaming temporary file(s):  ");
  3038. UPDATE_TERMINAL;
  3039.  
  3040. for(f=open_file; f<last_file; f++)
  3041.     if(f->previously_opened || f->ptr)
  3042.         {
  3043.         FILE *old_ptr = FOPEN(f->name, "r");
  3044.  
  3045.         if(f->ptr)
  3046.             fflush(f->ptr);
  3047.  
  3048.         if(old_ptr)
  3049.             @<Compare file contents@>@;
  3050.         else
  3051.             @<Rename the temporary file@>@; // No old file at all.
  3052.         }
  3053.  
  3054. if(!renamed) 
  3055.     printf("[no changes]");
  3056. }
  3057.  
  3058. @ The following code is patterned after \.{nuweb}'s.  It compares the
  3059. contents of the new, temporary file and the old one.  If they're the same,
  3060. the temporary file is deleted; otherwise, it overwrites the old file.
  3061.  
  3062. @<Compare file contents@>=
  3063. {
  3064. int c_old, c_new;
  3065. FILE *new_ptr;
  3066.  
  3067. if(f->ptr)
  3068.     new_ptr = freopen((CONST char *)f->tmp_name, "r", f->ptr);
  3069. else
  3070.     new_ptr = FOPEN(f->tmp_name, "r");
  3071.  
  3072. if(!new_ptr) 
  3073.     FATAL("\n!! Can't reopen temporary file ", f->tmp_name);
  3074.  
  3075. do
  3076.     {
  3077.     c_old = getc(old_ptr);
  3078.     c_new = getc(new_ptr);
  3079.     }
  3080. while(c_old == c_new && c_old != EOF);
  3081.  
  3082. fclose(old_ptr);
  3083. fclose(new_ptr);
  3084.  
  3085. if(c_old == c_new)
  3086.     remove((CONST char *)f->tmp_name); // Harmless if this doesn't work.
  3087. else
  3088.     @<Rename the temporary file@>@;
  3089. }
  3090.  
  3091. @ Since the behavior of |rename| is implementation-defined if the new file
  3092. exists, we explicitly remove it first.
  3093.  
  3094. @<Rename the temporary file@>=
  3095. {
  3096. /* Try to ensure that the following |rename| will succeed. */
  3097. remove((CONST char *)f->name); 
  3098.  
  3099. printf("(%s",f->name); // Echo to terminal.
  3100.  
  3101. if(rename((CONST char *)f->tmp_name, (CONST char *)f->name) != 0)
  3102.     { /* Rename didn't work.  Attempt to force the rename by issuing a
  3103. \.{mv} command.  The actual name of the command is obtained from the
  3104. preprocessor variable |MV|, which is defined on the command line and whose
  3105. value is ultimately defined in \.{defaults.mk}. */ 
  3106. #if ANSI_SYSTEM
  3107.     if(!system(NULL))
  3108.         { /* No command processor! */
  3109.         err_print(T, 
  3110. "Couldn't rename \"%s\" to \"%s\"", f->tmp_name, f->name);
  3111.         perror("");
  3112.         }
  3113.     else
  3114. #endif // |ANSI_SYSTEM|
  3115.         { 
  3116.         char temp[256];
  3117.  
  3118. /* We put the following here in case for some reason the make file can't
  3119. define |MV|.  This is the case with some versions of \.{nmake} on the PC. */
  3120. #ifndef MV
  3121.     #ifdef ibmpc
  3122.         #define MV rename
  3123.     #else
  3124.         #define MV mv
  3125.     #endif
  3126. #endif
  3127.         sprintf(temp, "%s %s %s", MV, f->tmp_name, f->name);
  3128.         system(temp);
  3129.         printf("*"); // Indicate a copy was done.
  3130.         }
  3131.     }
  3132.  
  3133. printf(")"); UPDATE_TERMINAL;
  3134.  
  3135. renamed = YES;
  3136. }
  3137.  
  3138. @ Here is a short-hand routine that expands the string equivalent of tokens
  3139. like |slash_slash| to the output.
  3140.  
  3141. @d OUT_OP(s) out_op(OC(s))
  3142. @d OUT_STR(s) out_str(OC(s))
  3143.  
  3144. @<Part 1@>=@[
  3145.  
  3146. SRTN out_op FCN((s))
  3147.     CONST outer_char HUGE *s C1("String to translate.")@;
  3148. {
  3149. out_str(s);
  3150.  
  3151. out_state = MISCELLANEOUS;
  3152. }
  3153.  
  3154. SRTN out_str FCN((s))
  3155.     CONST outer_char HUGE *s C1("")@;
  3156. {
  3157. while(*s) 
  3158.     C_putc(*s++);
  3159. }
  3160.  
  3161. @ Here we translate internal code to their external representations.
  3162.  
  3163. @d F_OP(op77,op88) (Fortran88 ? op88 : op77)
  3164.  
  3165. @<Cases like \.{!=}@>=
  3166.  
  3167. case plus_plus: 
  3168.     if(FORTRAN_LIKE(language))
  3169.         {
  3170.         @<Output `\.=' and left-hand side@>;
  3171.         buffer_out('+'); @+ buffer_out('1');
  3172.         out_state = MISCELLANEOUS;
  3173.         }
  3174.     else 
  3175.         {
  3176.         if(*(pC_buffer-1) == '+' && !nuweb_mode) 
  3177.             C_putc(' '); // Watch out for |x + ++y|.
  3178.         OUT_OP("++");
  3179.         }
  3180.  
  3181.     @<Mark split...@>@;
  3182.     break;
  3183.  
  3184. case minus_minus: 
  3185.     if(FORTRAN_LIKE(language))
  3186.         {
  3187.         @<Output `\.=' and left-hand side@>;
  3188.         buffer_out('-'); @+ buffer_out('1');
  3189.         out_state = MISCELLANEOUS;
  3190.         }
  3191.     else 
  3192.         {
  3193.         if(*(pC_buffer-1) == '-' && !nuweb_mode) 
  3194.             C_putc(' '); // Watch out for |x - --y|.
  3195.         OUT_OP("--");
  3196.         }
  3197.  
  3198.     @<Mark split...@>@;
  3199.     break;
  3200.  
  3201. case minus_gt: OUT_OP(FORTRAN_LIKE(language) ? ".EQV." : "->"); @+ break;
  3202.  
  3203. case gt_gt:
  3204.     @<Mark split...@>@;
  3205.      OUT_OP(">>"); @+ break;
  3206.  
  3207. case eq_eq: 
  3208.     @<Mark split...@>@;
  3209.     OUT_OP(R77_or_F ? F_OP(".EQ.","==") : "=="); @+ break;
  3210.  
  3211. case lt_lt: 
  3212.     @<Mark split...@>@;
  3213.     OUT_OP("<<"); @+ break;
  3214.  
  3215. case @'>':
  3216.     if(in_string) 
  3217.         out_dflt(cur_char);
  3218.     else 
  3219.         OUT_OP(R77_or_F ? F_OP(".GT.",">") : ">"); 
  3220.  
  3221.     @<Mark split...@>@;
  3222.     break;
  3223.  
  3224. case gt_eq: 
  3225.     OUT_OP(R77_or_F ? F_OP(".GE.",">=") : ">=");  
  3226.     @<Mark split...@>@;
  3227.     break;
  3228.  
  3229. case @'<':
  3230.     if(in_string) 
  3231.         out_dflt(cur_char);
  3232.     else 
  3233.         OUT_OP(R77_or_F ? F_OP(".LT.","<") : "<"); 
  3234.  
  3235.     @<Mark split...@>@;
  3236.     break;
  3237.  
  3238. case lt_eq: 
  3239.     OUT_OP(R77_or_F ? F_OP(".LE.","<=") : "<="); 
  3240.     @<Mark split...@>@;
  3241.     break;
  3242.  
  3243. case not_eq: 
  3244.     OUT_OP(R77_or_F ? F_OP(".NE.","<>") : "!="); 
  3245.     @<Mark split...@>@;
  3246.     break;
  3247.  
  3248. case and_and: 
  3249.     OUT_OP(R77_or_F ? ".AND." : "&&"); 
  3250.     @<Mark split...@>@;
  3251.     break;
  3252.  
  3253. case or_or: 
  3254.     if(language==TEX) meta_mode = YES;
  3255.     else
  3256.         {
  3257.         OUT_OP(R77_or_F ? ".OR." : "||"); 
  3258.         @<Mark split...@>@;
  3259.         }
  3260.     break;
  3261.  
  3262. case star_star: 
  3263.     if(language==TEX) meta_mode = NO;
  3264.     else OUT_OP(C_LIKE(language) ? "^^" : "**"); 
  3265.     break;
  3266.  
  3267. case @'!': 
  3268.     @<Mark split...@>@;
  3269.     if(in_string) 
  3270.         return out_dflt(cur_char);
  3271.     else 
  3272.         OUT_OP(R77_or_F ? ".NOT." : "!"); 
  3273.     break; 
  3274.  
  3275. case slash_slash: OUT_OP("//"); @+ break;
  3276.  
  3277. case colon_colon: 
  3278.     if(in_string) 
  3279.         return out_dflt(cur_char);
  3280.     else 
  3281.         OUT_OP("::");
  3282.     break;
  3283.  
  3284. case ellipsis: 
  3285.     OUT_OP(FORTRAN_LIKE(language) ? ".NEQV." : "...");
  3286.     @<Mark split...@>@;
  3287.     break;
  3288.  
  3289. case paste: OUT_OP("##"); @+ break;
  3290.  
  3291. case dot_const: 
  3292.     C_putc('.'); 
  3293.     STRCPY(dot_op.name+1,dots[cur_val].symbol);
  3294.     to_outer(dot_op.name+1);
  3295.     OUT_OP(OC(dot_op.name+1));
  3296.     C_putc('.');
  3297.     break;
  3298.  
  3299. @ Here we endow \Ratfor-77 with C's ability to handle powerful assignment
  3300. operators. Expressions like |i *= expr| get translated into |i = i*(expr)|.
  3301. @<Cases like \.{+=}@>=
  3302. case @'+':
  3303. case @'-':
  3304. case @'*':
  3305. case @'/':
  3306. /* These operators are handled in \Tangle\ as two adjacent tokens; we have
  3307. to check for that, and we dare not be in |VERBATIM| mode. */
  3308.     if(!FORTRAN_LIKE(language) || 
  3309.             cur_byte == cur_end || *cur_byte != @'=' ||  
  3310.             out_state == VERBATIM || !xpn_Ratfor)
  3311.         {
  3312.         if(cur_char==@'*' && C_LIKE(language) && out_state != VERBATIM
  3313.                 && *(pC_buffer-1) == '/' && !nuweb_mode) 
  3314.             C_putc(' '); // Watch out for |x/ *p|; not a comment.
  3315.         @<Mark split...@>@;
  3316.         return out_dflt(cur_char);
  3317.         }
  3318.  
  3319.     cur_byte++; /* Skip over the `\.='. */
  3320.     @<Output `\.=' and left-hand side@>;
  3321.     out_dflt(cur_char);
  3322.     C_putc('(');
  3323.     send_rp = YES; /* The enclosing right paren will be output when the
  3324. next newline is encountered. */
  3325.     break;
  3326.  
  3327. @ This fragment is used both above and for the |++| and |--|~operators.
  3328. @<Output `\.=' and left-hand side@>=
  3329. @{
  3330. outer_char HUGE *l;
  3331.  
  3332. @b
  3333. /* The left-hand side has already been output. */
  3334. buffer_out('=');
  3335. out_state = MISCELLANEOUS;
  3336.  
  3337. /* Now output the |i|~in the above
  3338. example again; however, in general, that could be subscripted etc. */
  3339. if(compound_assignments)
  3340.     {
  3341.     if(last_xpr_overflowed) 
  3342.         OVERFLW("last expression",ABBREV(max_expr_chars)); 
  3343.  
  3344.     for(l=last_char; isdigit(*l) || !isalpha(*l); l++)
  3345.         ;
  3346.  
  3347.     if(plast_char - l >= 3 && STRNCMP(last_char, "if(", 3) == 0)
  3348.         ERR_PRINT(T, "Sorry, can't expand compound assignment \
  3349. operators correctly after simple IF; use an IF...THEN construction");
  3350.  
  3351.     while(l < plast_char)
  3352.         buffer_out(*l++);
  3353.     }
  3354. else FATAL("!! Operators ++, --, +=, -=, *=, and /= are not allowed; \
  3355. they were turned off by option \"-+\".","");
  3356. }
  3357.  
  3358. @ This important fragment translates the internal code for an identifier
  3359. into the actual name. Macro expansion and \Ratfor\ token translation is
  3360. done here.
  3361. @<Case of an identifier@>=
  3362. case end_format_stmt:
  3363.     in_format = NO;
  3364.     C_putc(';');
  3365.     out_state = NUM_OR_ID;
  3366.     break;
  3367.  
  3368. case begin_format_stmt:
  3369.     in_format = YES;
  3370.     OUT_OP(" format");
  3371.     out_state = MISCELLANEOUS;
  3372.     break;
  3373.  
  3374. case identifier:
  3375.     cur_char = x_identifier(cur_char);
  3376.     break;
  3377.  
  3378.  
  3379. @ This routine was inserted to attempt to cut down the function length.
  3380. @<Part 1@>=@[
  3381.  
  3382. eight_bits x_identifier FCN((cur_char))
  3383.     eight_bits cur_char C1("")@;
  3384. {
  3385. @<Possibly expand special keyword@>;
  3386.  
  3387. if(is_deferred((sixteen_bits)cur_val)) return cur_char;
  3388.  
  3389. /* |MAC_LOOKUP| determines whether this is a WEB macro. Eventually, this
  3390. routine will be called recursively to output the expansion. The |in_macro|
  3391. flag prevents us from checking the expanded tokens again, since everything
  3392. will already have been expanded. */
  3393.   if(!mac_protected && (macro_text=MAC_LOOKUP(cur_val)) != NULL)
  3394.     {
  3395.     @<Output a macro expansion@>@;
  3396.     return cur_char;
  3397.     }
  3398. else 
  3399.     { /* Not a macro. */
  3400.     @<Mark split...@>@;
  3401.     if (out_state==NUM_OR_ID && !nuweb_mode) 
  3402.         C_putc(' ');
  3403.  
  3404.     @<Output a possibly truncated identifier@>;
  3405.  
  3406.     if(no_expand)
  3407.         no_expand = mac_protected = NO;
  3408.     }
  3409.  
  3410. end_identifier:
  3411.   out_state = in_format ? MISCELLANEOUS : NUM_OR_ID; 
  3412.  
  3413. return cur_char;
  3414. }
  3415.  
  3416. @ It is easy to check whether an identifier is a deferred macro, because
  3417. the |macro_type| field was set when the deferred macro was stored in the
  3418. deferred pool.  If it is, the macro definition is executed and the macro is
  3419. now recorded as a regular (immediate) one.
  3420.  
  3421. @<Part 1@>=@[
  3422.  
  3423. boolean is_deferred FCN((cur_val))
  3424.     sixteen_bits cur_val C1("")@;
  3425. {
  3426. name_pointer np;
  3427.  
  3428. np = name_dir + cur_val;
  3429.  
  3430. if(np->macro_type == DEFERRED_MACRO)
  3431.     {
  3432.     text_pointer tp;
  3433.     eight_bits HUGE *p0;
  3434.     eight_bits a0;
  3435.  
  3436.     tp = (text_pointer)np->equiv; /* Position in the deferred pool. */
  3437.  
  3438. /* Copy the tokens of the definition over into the next text. */
  3439.     for(p0=tp->tok_start; p0 < (tp+1)->tok_start; ) 
  3440.         if(TOKEN1(a0= *p0++))
  3441.             if(a0 == @'#')
  3442.                    switch(*p0)
  3443.                 {
  3444.                 case @'!':
  3445.                     if(*(p0+1) == MACRO_ARGUMENT)
  3446.                         app_repl(a0)@; 
  3447.                 else @<Copy but don't expand deferred macro@>@;
  3448.                 break;
  3449.     
  3450.                 default:
  3451.                     app_repl(a0);
  3452.                     break;
  3453.                 }
  3454.             else app_repl(a0)@; /* Single token, not special. */
  3455.         else
  3456.             {
  3457.             app_repl(a0);
  3458.             app_repl(*p0++);
  3459.             }
  3460.  
  3461.     cur_text = text_ptr;
  3462.     cur_text->Language = (boolean)language;
  3463.     cur_text->nargs = tp->nargs;
  3464.     cur_text->moffset = tp->moffset;
  3465.     cur_text->var_args = tp->var_args;
  3466.     cur_text->recursive = NO;
  3467.     cur_text->text_link = macro;
  3468.  
  3469.     (++text_ptr)->tok_start = tok_ptr;
  3470.  
  3471.     np = name_dir + IDENTIFIER(tp->tok_start[0],tp->tok_start[1]);
  3472.     np->macro_type = IMMEDIATE_MACRO; // Now the defn's been executed.
  3473.     np->equiv = (EQUIV)cur_text;
  3474.  
  3475.     return YES; // It's a deferred macro.
  3476.     }
  3477.  
  3478. return NO; // Not a deferred macro.
  3479. }
  3480.     
  3481. @
  3482. @<Copy but don't expand deferred macro@>=
  3483. {
  3484. if(TOKEN1(*++p0)) MACRO_ERR("! Macro token `#!' must be followed by \
  3485. identifier",YES); 
  3486. else
  3487.     {
  3488.     text_pointer m;
  3489.  
  3490.     if( (m=MAC_LOOKUP(IDENTIFIER(*p0,*(p0+1)))) == NULL)
  3491.         MACRO_ERR("! Expecting macro identifier after \"#!\"",YES);
  3492.     else 
  3493.         if(m->nargs > 0)
  3494.             MACRO_ERR("! Macro after \"#!\" can't have arguments",
  3495.                 YES);
  3496.         else @<Copy tokens of macro@>@;
  3497.  
  3498.     p0 += 2;
  3499.     }
  3500. }
  3501.  
  3502. @<Unused@>=
  3503. {
  3504. SPEC *s;
  3505.  
  3506. for(s=spec_tokens; s->len != 0; s++)
  3507.     if(cur_val == *s->pid && s->expand != NULL) 
  3508.         {
  3509.         boolean in_macro0 = in_macro;
  3510.  
  3511.         in_macro = NO; /* Don't suppress recursive expansion of
  3512. macros. */
  3513.         (*s->expand)();
  3514.         in_macro = in_macro0;
  3515.  
  3516.         goto end_identifier;
  3517.         }
  3518. }
  3519.  
  3520. @ Expand a \Ratfor\ token if necessary. 
  3521.  
  3522. @<Possibly expand spec...@>=
  3523. {
  3524. boolean in_macro0 = in_macro;
  3525. name_pointer np = name_dir + cur_val;
  3526. X_FCN (HUGE_FCN_PTR *pf)(VOID); /* The function associated with expandable
  3527.                     keywords. */
  3528.  
  3529. if(np->expandable & language)
  3530.     {
  3531. expand_special:
  3532.     in_macro = NO; /* Don't suppress recursive expansion of macros. */
  3533.  
  3534.     pf = np->x_translate[lan_num(language)];
  3535.  
  3536.     if(pf) (*pf)(); // Expand keyword.
  3537.     else CONFUSION("possibly expand special",
  3538.         "Allegedly expandable keyword has no associated function");
  3539.  
  3540.     in_macro = in_macro0;
  3541.  
  3542.     cur_char = id_keyword; /* Helps \Ratfor\ know what happened. */
  3543.     goto end_identifier;
  3544.     }
  3545. else if(R77 && Fortran88 && !checking_label) 
  3546.     switch(chk_lbl())
  3547.         {
  3548.        case YES: goto expand_special;
  3549.           case -1:  goto end_identifier;
  3550.        case NO: break;
  3551.         }
  3552. }
  3553.  
  3554. @ At this point in the output routine, we have identified an identifier as
  3555. a macro. Expand it, and output it recursively.
  3556. @<Output a macro exp...@>=
  3557. @{
  3558. eight_bits HUGE *p1;
  3559.  
  3560. @b
  3561. in_macro = YES; /* Used as a flag to prevent |MAC_LOOKUP| on
  3562.     recursive |out_char| output of the final translated macro. */
  3563.  
  3564. p1 = xmacro(macro_text,&cur_byte,cur_end,macrobuf); /* Expand this
  3565.     macro into the macro buffer. The final expansion will begin at |p1|
  3566.     and end at~|mp|. */
  3567.  
  3568. /* Output final translated text, which begins at the end~|p1| of the last
  3569. translation and ends at the current value of~|mp|. This calls |out_char|
  3570. recursively.  */
  3571. copy_out(p1,mp,macro);
  3572. in_macro = NO;
  3573. }
  3574.  
  3575. @ We want the speediest possible output routine, so we bypass extra stuff
  3576. if no variables were truncated.
  3577. @<Output a poss...@>=
  3578. @{
  3579. name_pointer np;
  3580.  
  3581. @b
  3582. np = name_dir + cur_val;
  3583.  
  3584. if(truncate_ids) 
  3585.     out_trunc(np);
  3586. else 
  3587.     see_id(np->byte_start,(np+1)->byte_start);
  3588. }
  3589.  
  3590. @ Interface to \.{rat77.web}.
  3591. @<Part 1@>=@[
  3592.  
  3593. SRTN out_ptrunc FCN((cur_val))
  3594.     sixteen_bits cur_val C1("")@;
  3595. {
  3596. @<Output a poss...@>@;
  3597. }
  3598.  
  3599. @ Write out an identifier, translating from internal |ASCII|.
  3600. @<Part 1@>=@[
  3601.  
  3602. SRTN see_id FCN((start,end))
  3603.     CONST ASCII HUGE *start C0("Beginning of identifier name.")@;
  3604.     CONST ASCII HUGE *end C1("End of identifier name.")@;
  3605. {
  3606. CONST ASCII HUGE *j;
  3607.  
  3608. for (j=start; j<end; j++) C_putc(XCHR(*j));
  3609. }
  3610.  
  3611. @ Print the $n$-th~identifier for debugging purposes. Call this routine
  3612. from the debugger.
  3613. @<Part 1@>=@[
  3614.  
  3615. int id FCN((n))
  3616.     int n C1("Identifier number.")@;
  3617. {
  3618. printf(_Xx("Id %d (0x%x): \"%s\"\n"),n,n,name_of((sixteen_bits)n));
  3619. return n;
  3620. }
  3621.  
  3622. @ This function translates internal text to the outer world, possibly
  3623. truncating it. 
  3624.  
  3625. @<Part 1@>=@[
  3626.  
  3627. outer_char HUGE *name_of FCN((id0))
  3628.     sixteen_bits id0 C1("Identifier token whose name is sought.")@;
  3629. {
  3630. static ASCII temp[MAX_ID_LENGTH];
  3631. int k,n;
  3632. name_pointer np;
  3633. CONST ASCII HUGE *end;
  3634.  
  3635. np = name_dir + id0;
  3636.  
  3637. /* Don't choke on bad id. */
  3638. if(np >= name_ptr)
  3639.     {
  3640.     STRCPY(temp,"???");
  3641.     return (outer_char HUGE *)temp;
  3642.     }
  3643.  
  3644. PROPER_END(end);
  3645.  
  3646. #if 0 /* This construction gives a compiler error on the IBM/6000. */
  3647. n = MIN(end - np->byte_start,MAX_ID_LENGTH-1);
  3648. #else
  3649. if(end - np->byte_start < MAX_ID_LENGTH - 1)
  3650.     n = end - np->byte_start;
  3651. else
  3652.     n = MAX_ID_LENGTH - 1;
  3653. #endif
  3654.  
  3655. STRNCPY(temp,np->byte_start,n);
  3656.  
  3657. /* We must be careful when breakpointing; backslashes must be escaped. */ 
  3658. if(breakpoints)
  3659.    for(k=0; k<n; k++)
  3660.     if(temp[k] == @'\\') temp[k] = @'/';
  3661.  
  3662. temp[n] = '\0';
  3663.  
  3664. return to_outer(temp);
  3665. }
  3666.  
  3667. @ Spit out a possibly truncated identifier.
  3668.  
  3669. @<Part 1@>=@[
  3670.  
  3671. CONST ASCII HUGE *proper_end FCN((np))
  3672.     name_pointer np C1("")@;
  3673. {
  3674. CONST ASCII HUGE *end;
  3675.  
  3676. PROPER_END(end);
  3677. return end;
  3678. }
  3679.  
  3680. SRTN out_trunc FCN((np))
  3681.     CONST name_pointer np C1("")@;
  3682. {
  3683. TRUNC HUGE *s;
  3684. ASCII HUGE *pc;
  3685.  
  3686. pc = np->byte_start;
  3687.  
  3688. if(*pc != BP_MARKER)
  3689.         { /* Not truncated. */
  3690.         CONST ASCII HUGE *end;
  3691.  
  3692. /* If the next one was truncated, recover the proper end location. */
  3693.         PROPER_END(end);
  3694.         see_id((CONST ASCII HUGE *)pc,end);
  3695.         }
  3696.     else 
  3697.         { /* Truncated. */
  3698.         s = ((BP HUGE *)pc)->Root;
  3699.         see_id(s->id,s->id_end);
  3700.         }
  3701. }
  3702.  
  3703. @ Every time the line number is printed, it's remembered to help out with
  3704. error messages.
  3705. @<Glob...@>=
  3706.  
  3707. EXTERN LINE_NUMBER nearest_line SET(0);
  3708.  
  3709. @ Here we write out the module number info. If |cur_val > 0|, we're
  3710. beginning a module; if |cur_val < 0|, we're ending a module; if it's zero,
  3711. we print out the line number. The |line_info| flag kills off the output of
  3712. this information (although presently the information is still retained in
  3713. the file).
  3714.  
  3715. @<Case of a mod...@>=
  3716.  
  3717. case module_number:
  3718.     if (cur_val > 0) prn_mod_num(OC("%c* %ld: *%c"),cur_val); // Beginning.
  3719.     else if(cur_val < 0) prn_mod_num(OC("%c* :%ld *%c"),cur_val); // End.
  3720.     else 
  3721.         {// Print out the line number; remember it for error messages.
  3722.         if(line_info)
  3723.             {
  3724.             nearest_line = BASE2* (*cur_byte++);
  3725.             nearest_line += *cur_byte++; // Gets the line number.
  3726.  
  3727.                 C_sprintf(OC("\n%cline %u \""),2,
  3728.                 language==TEX ? '%' : '#',nearest_line);
  3729.  
  3730. /* Get pointer to file name. */
  3731.                 cur_val = BASE2* (*cur_byte++); 
  3732.             cur_val += *cur_byte++;
  3733.  
  3734.             @<Output a possibly truncated identifier@>@;
  3735.             C_sprintf(OC("\"\n"),0);
  3736.             }
  3737.  
  3738. @%        C_sprintf(OC("\n"),0);
  3739.         }
  3740.  
  3741.     break;
  3742.  
  3743. @ The following function writes to the output file a comment about
  3744. beginning or ending a section (distinguished by the sign of~|cur_val|).
  3745. @<Part 1@>=@[
  3746.  
  3747. SRTN prn_mod_num FCN((fmt,val))
  3748.     outer_char *fmt C0("")@;
  3749.     long val C1("")@;    
  3750. {
  3751. int l;
  3752.  
  3753. if(line_info)
  3754.     {
  3755.     l = lan_num(R77_or_F && !free_90 ? FORTRAN : language);
  3756.  
  3757.     if(val < 0) 
  3758.         { /* Ending a section. */
  3759.         val = -val;
  3760. @%        C_putc('\n');
  3761.         }
  3762.  
  3763.     if(FORTRAN_LIKE(language)) 
  3764.         {
  3765.         if(out_pos > rst_pos) flush_out(YES);
  3766.         out_pos = 0;
  3767.         }
  3768.  
  3769.     C_sprintf(fmt,3,begin_comment_char[l],val,end_comment_char[l]);
  3770.     }
  3771. @#if 0
  3772.     switch(language)
  3773.         {
  3774.         case C:
  3775.         case C_PLUS_PLUS:
  3776.         case TEX:
  3777.         case LITERAL:
  3778.          C_sprintf(fmt,3,
  3779.             begin_comment_char[l],val,end_comment_char[l]);
  3780.         break;
  3781.  
  3782.         case RATFOR:
  3783.         case RATFOR_90:
  3784.         case FORTRAN:
  3785.         case FORTRAN_90:
  3786.         CHECK_OPEN;
  3787.         fprintf(out_file,fmt,
  3788.             begin_comment_char[l],val,end_comment_char[l]);
  3789.         break;
  3790.  
  3791.         default:
  3792.         ;
  3793.         }
  3794. @#endif
  3795. }
  3796.  
  3797. @* INTRODUCTION to the INPUT PHASE.  We have now seen that \.{TANGLE} will
  3798. be able to output the full \cee\ program, if we can only get that program
  3799. into the byte memory in the proper format. The input process is something
  3800. like the output process in reverse, since we compress the text as we read
  3801. it in and we expand it as we write it out.
  3802.  
  3803. There are three main input routines. The most interesting is |get_next|,
  3804. which gets the next token of a code text; the other two are used to scan
  3805. rapidly past \TeX\ text in the \.{WEB} source code. |skip_ahead| will jump
  3806. to the next token that starts with `\.{@@}'; |skip_comment| skips to the
  3807. end of a comment.
  3808.  
  3809. @i t_codes.hweb
  3810.  
  3811. @<Global...@>=
  3812.  
  3813. IN_STYLE eight_bits ccode[128]; // Meaning of a char following '\.{@@}'.
  3814.  
  3815. @  The control codes are assigned in \.{style.web}.
  3816.  
  3817. @m TANGLE_ONLY(d,c) INI_CCODE(d,c)
  3818. @m WEAVE_ONLY(d,c) INI_CCODE(d,USED_BY_OTHER)
  3819.  
  3820. @<Set ini...@>= 
  3821.  
  3822. zero_ccodes(); /* See \.{style.web}. */
  3823. ccode[@'/'] = begin_vcmnt; /* The commenting style is also fundamental, and
  3824.     for convenience the |line_break| command is also inviolate. (For
  3825.     \FTANGLE, this gets reassigned later.) */
  3826.  
  3827. @<Set the changable codes@>@;
  3828. @<Reassign certain codes for \FTANGLE@>@;
  3829. prn_codes();
  3830.  
  3831. @ Here are the default values for the things that are allowed to be
  3832. changed. Codes that are used only by
  3833. \FWEAVE\ get the special code~|ignore|; these are just skipped.  Codes
  3834. that are used by neither processor are initialized to~|'0xFF'|; that can be
  3835. used to trigger an error message.
  3836. Those things that must be reassigned for \FTANGLE\ are here
  3837. assigned the code for \FWEAVE; they're changed later by the |reassign|
  3838. function.  
  3839. @<Set the changable...@>= 
  3840. SAME_CCODE(" \t*",new_module);
  3841.  
  3842. SAME_CCODE("aA",begin_code);
  3843. SAME_CCODE("<",module_name);
  3844.  
  3845. SAME_CCODE("dD",definition);
  3846. SAME_CCODE("uU",undefinition);
  3847. SAME_CCODE("mM",WEB_definition);
  3848. SAME_CCODE("fF",formatt);
  3849.  
  3850. SAME_CCODE("'\"",ascii_constant);
  3851. REASSIGNABLE("=",verbatim);
  3852.  
  3853. REASSIGNABLE("tT",TeX_string);
  3854.  
  3855. SAME_CCODE("L",L_switch);
  3856. SAME_CCODE("cC",begin_C);
  3857. SAME_CCODE("rR",begin_RATFOR);
  3858. SAME_CCODE("n",begin_FORTRAN);
  3859. SAME_CCODE("N",begin_nuweb);
  3860.  
  3861. SAME_CCODE("&",join);
  3862.  
  3863. SAME_CCODE("?",Compiler_Directive);
  3864. SAME_CCODE("%",invisible_cmnt);
  3865.  
  3866. /* The next three must be reassigned to |control_text|. */
  3867. REASSIGNABLE("^",xref_roman);
  3868. REASSIGNABLE(".",xref_typewriter);
  3869. REASSIGNABLE("9",xref_wildcard);
  3870.  
  3871. SAME_CCODE("#",big_line_break);
  3872.  
  3873. SAME_CCODE("(",begin_meta);
  3874. SAME_CCODE(")",end_meta);
  3875.  
  3876. SAME_CCODE("l",limbo_text);
  3877. SAME_CCODE("vV",op_def);
  3878. SAME_CCODE("wW",macro_def);
  3879.  
  3880. TANGLE_ONLY("{",begin_bp);
  3881. TANGLE_ONLY("}bB",insert_bp);
  3882.  
  3883. TANGLE_ONLY("!",no_mac_expand);
  3884.  
  3885. SAME_CCODE("oO",new_output_file);
  3886.  
  3887. WEAVE_ONLY("\001",toggle_output); // This command is for internal use only!
  3888. WEAVE_ONLY("\\",force_line);
  3889. WEAVE_ONLY("_",underline);
  3890. WEAVE_ONLY("[",defd_at);
  3891. WEAVE_ONLY("`]",implicit_reserved);
  3892. WEAVE_ONLY("$",switch_math_flag);
  3893. {
  3894. char temp[3];
  3895.  
  3896. sprintf(temp,";%c",XCHR(interior_semi));
  3897. WEAVE_ONLY(temp,pseudo_semi);
  3898. }
  3899. WEAVE_ONLY("e",pseudo_expr);
  3900. WEAVE_ONLY(":",pseudo_colon);
  3901. WEAVE_ONLY(",",thin_space);
  3902. WEAVE_ONLY("|",math_break);
  3903. WEAVE_ONLY("+",no_line_break);
  3904. WEAVE_ONLY("-",no_index);
  3905. WEAVE_ONLY("~",yes_index);
  3906. #if(DEBUG)
  3907.     WEAVE_ONLY("012",trace);
  3908. #endif /* |DEBUG| */
  3909. }
  3910.  
  3911. @ For \FTANGLE, certain codes must be reassigned (after they've possibly
  3912. been overridden by the style file).
  3913. @<Reassign...@>=
  3914. {
  3915. reassign(xref_roman,control_text);
  3916. reassign(xref_typewriter,control_text);
  3917. reassign(xref_wildcard,control_text);
  3918. reassign(TeX_string,control_text);
  3919.  
  3920. reassign(verbatim,stringg);
  3921. }
  3922.  
  3923. @ The |skip_ahead| procedure reads through the input at fairly high speed
  3924. until finding the next non-ignorable control code, which it returns.  There
  3925. is one special nuance. We don't want to process a language change between
  3926. vertical bars. Since during the high-speed scan we don't keep track of
  3927. balanced bars, we assume that the combination of bar followed by possible
  3928. spaces followed by a language command means the start of a barred section,
  3929. and we skip over the language command in that case.
  3930.  
  3931. @d MAYBE_SET_OUTPUT(l) if(last_char != @'|') set_output_file(l)
  3932.  
  3933. @<Part 1@>=@[
  3934.  
  3935. eight_bits skip_ahead FCN((last_control,skip_over_bars))
  3936.     eight_bits last_control C0("Last token that was seen.")@;
  3937.     boolean skip_over_bars C1("")@;
  3938. {
  3939. eight_bits cc; // Control code found.
  3940. int ncc = 0; /* A counter that counts the \.{@@}s;
  3941.         used to figure out whether to ignore section
  3942.         names immediately after \.{@@f}. */
  3943. ASCII last_char;
  3944. ASCII HUGE *lc;
  3945. ASCII HUGE *l1 = limit + 1;
  3946.  
  3947. WHILE()
  3948.     {
  3949.     if (loc>limit)
  3950.         {
  3951. another_line:
  3952.         if(from_buffer) 
  3953.             {
  3954.             undivert(); // Switch back to reading from files.
  3955.             return ignore;
  3956.             }
  3957.         else 
  3958.             {
  3959.             if(!get_line()) 
  3960.                 return new_module;
  3961.  
  3962.             l1 = limit + 1;
  3963.             }
  3964.         }
  3965.  
  3966.     *l1 = @'@@'; // Barrier to stop high-speed scan through line.
  3967.  
  3968. more_stuff:
  3969.     switch(*loc)
  3970.         {
  3971.        case @'@@':
  3972.         break;
  3973.  
  3974.        case @'|':
  3975.         if(skip_over_bars)
  3976.             {
  3977.             if(skip_bars() == new_module) return new_module;
  3978. /* It's now positioned after the bar. */
  3979.             continue;
  3980.             }
  3981.  
  3982. /* Otherwise, we're in limbo or scanning control text; just keep going. */
  3983.  
  3984.        default:
  3985.         loc++;
  3986.         if(loc > limit)
  3987.             {
  3988.             ncc = 2;
  3989.             goto another_line;
  3990.             }
  3991.         goto more_stuff;
  3992.         }
  3993.  
  3994.     *l1 = @' '; // Reset line terminator.
  3995.  
  3996.     if(loc > limit) ncc = 2;
  3997.     else @<Return the next non-ignorable control code@>@;
  3998.     }
  3999.  
  4000. DUMMY_RETURN(ignore);
  4001. }
  4002.  
  4003. @
  4004. @<Part 1@>=@[
  4005.  
  4006. eight_bits skip_bars(VOID)
  4007. {
  4008. PARAMS params0;
  4009. LANGUAGE language0 = language;
  4010. eight_bits ret_val;
  4011.  
  4012. params0 = params; // Save state.
  4013.  
  4014. loc++; // Advance past the opening bar.
  4015.  
  4016. WHILE()
  4017.     {
  4018.     if(loc > limit && !get_line()) 
  4019.         {
  4020.         err_print(W,"Reached end of file while skipping code text %s", 
  4021.             BTRANS);
  4022.         ret_val = new_module;
  4023.         goto done;
  4024.         }
  4025.  
  4026.     switch(next_control=get_next())
  4027.         {
  4028.        case begin_bp:
  4029.        case insert_bp:
  4030.        case begin_meta:
  4031.        case end_meta:
  4032.        case formatt:
  4033.        case limbo_text:
  4034.        case op_def:
  4035.        case macro_def:
  4036.        case definition:
  4037.        case undefinition:
  4038.        case WEB_definition:
  4039.        case m_ifdef:
  4040.        case m_ifndef:
  4041.        case m_else:
  4042.        case m_elif:
  4043.        case m_endif:
  4044.        case m_for:
  4045.        case m_endfor:
  4046.        case m_pragma:
  4047.        case m_undef:
  4048.        case begin_code:
  4049.         err_print(T,"Control code not allowed within |...|; \
  4050. inserted '|' in %s", MTRANS);
  4051.         loc -= 2;
  4052.         ret_val = @'|';
  4053.         goto done;
  4054.  
  4055.        case new_module:
  4056.         err_print(T,"Module%s ended while skipping code text; \
  4057. inserted '|'", MTRANS0); // Falls through to next case!
  4058.  
  4059.        case @'|':
  4060.         ret_val = next_control;
  4061.         goto done;
  4062.         }        
  4063.     }
  4064.  
  4065. done:
  4066.   params = params0;
  4067.   frz_params();
  4068.   set_output_file(language0);
  4069.  
  4070. return ret_val;
  4071. }
  4072.  
  4073. @ We get to here while skipping through a line at high speed.
  4074. @<Return the next non-ignorable...@>=
  4075. {
  4076. last_char = @' '; // Get the last non-blank character before this control code.
  4077.  
  4078. for(lc=loc-1; lc>=cur_buffer; lc--)
  4079.     if(*lc != @' ') 
  4080.         {
  4081.         last_char = *lc; // This might be a vertical bar.
  4082.         break;
  4083.         }
  4084.  
  4085. ++loc; // Position to after the~\.{@@}.
  4086. ++ncc; // Count the \.{@@}s.
  4087.  
  4088. switch(cc=ccode[*(loc++)])
  4089.     { /* Position to after \.{@@?}. */
  4090.    @<Specific language cases@>:
  4091.     loc--; /* Position to language letter; fall through. */
  4092.  
  4093.    case L_switch:
  4094.     {
  4095.     if(last_char != @'|')
  4096.         {
  4097.         @<Set |language|@>@;
  4098.         if(module_count == 0) global_params = params;
  4099.         set_output_file(language);
  4100.         }
  4101.     continue;
  4102.     }
  4103.     
  4104.    case begin_nuweb:
  4105.     nuweb_mode1 = nuweb_mode = !NUWEB_MODE;
  4106.     if(module_count == 0) global_params = params;
  4107.     continue;
  4108.  
  4109.    case control_text:
  4110.     while ((c=skip_ahead(ignore,NO))==@'@@');
  4111.       /* only \.{@@@@} and \.{@@>} are expected */ /* Is |c| used?? */
  4112.  
  4113.       if (*(loc-1)!=@'>') ERR_PRINT(T,"Improper @@ within control text");
  4114. @.Improper \AT! within control text@>
  4115.     continue;
  4116.  
  4117.    case compiler_directive:
  4118.    case Compiler_Directive:
  4119.     if(scanning_TeX)
  4120.         ERR_PRINT(T,"Compiler directives are allowed only in code");
  4121.     loc = limit + 1;
  4122.     continue;
  4123.  
  4124.    case invisible_cmnt:
  4125.     loc = limit + 1;
  4126.     continue;
  4127.  
  4128.    case module_name:
  4129.     if(ncc==1 && last_control==formatt) 
  4130.         {
  4131.         loc -= 2;
  4132.         get_next(); // Scan module name to get it into table.
  4133.         continue;
  4134.         }
  4135.     break;
  4136.  
  4137.    case big_line_break: /* \.{@@\#} */
  4138.     if(loc >= limit) continue;
  4139.  
  4140.     @<Process possible preprocessor command@>; // (See \.{typedefs.web}.)
  4141.     continue;
  4142.  
  4143.    case USED_BY_NEITHER:
  4144.     err_print(T,"Invalid `@@%c' ignored",XCHR(*(loc-1)));
  4145.     continue;
  4146.     }
  4147.  
  4148. if (cc!=ignore || (*(loc-1)==@'>' && (ncc!=2) && last_control != formatt) ) 
  4149.     return cc; // \.{@@}~code or end of module name.
  4150. }
  4151.  
  4152. @ The |skip_comment| procedure reads through the input at somewhat high
  4153. speed until finding the end-comment token~`\.{*/}' or a new-line, in which
  4154. case |skip_comment| will be called again by |get_next|, since the comment
  4155. is not finished.  This is done so that the each newline in the code part of
  4156. a module is copied to the output; otherwise the \&{\#line} commands
  4157. inserted into the output file by the output routines become useless.  If it
  4158. comes to the end of the module it prints an error message.
  4159.  
  4160. @<Global...@>=
  4161.  
  4162. EXTERN boolean comment_continues SET(NO); // Are we scanning a comment?
  4163.  
  4164. @ Skip over comments.
  4165. @<Part 2@>=@[
  4166.  
  4167. boolean skip_comment(VOID)
  4168. {
  4169. ASCII c; /* current character */
  4170. PARSING_MODE outer_mode;
  4171.  
  4172. outer_mode = parsing_mode;
  4173. parsing_mode = OUTER;
  4174.  
  4175. if(comment_continues) loc--; /* We've already scanned over white space, so
  4176.   |loc| is presently one position beyond the first non-blank character on the
  4177.   continuation line. */
  4178. else if(*(loc-1) == @'/') loc++; /* If we're starting a comment, |loc|~is
  4179.   positioned on the star; move past that. */
  4180.  
  4181. WHILE()
  4182.     {
  4183.     if (loc>limit)
  4184.        if(!long_comment) @<Finish skipping comment and |break|@>@;
  4185.        else if(get_line()) 
  4186.         {
  4187.         comment_continues = YES; 
  4188.         break;
  4189.         }
  4190.           else
  4191.         {
  4192.             err_print(T,"Input ended in middle of comment %s", BTRANS);
  4193. @.Input ended in mid-comment@>
  4194.         comment_continues=NO;
  4195.         break;  /* We |break| out and return so |get_next| can
  4196. return a newline. */
  4197.         }
  4198.  
  4199.     c = *(loc++);
  4200.  
  4201.     if (c==@'*' && *loc==@'/') 
  4202.     { 
  4203.     loc++;
  4204.     @<Finish skipping comment...@>@;
  4205.     }
  4206.  
  4207.     if (c==@'@@') 
  4208.     {
  4209.           if (ccode[*loc]==new_module) /* `\.{@@\ }' or `\.{@@*}' */
  4210.          {
  4211.             err_print(T,"Section name ended in middle of comment %s", 
  4212.             BTRANS); 
  4213.         loc--;
  4214. @.Section name ended in mid-comment@>
  4215.         @<Finish skipping comment...@>@;
  4216.           }
  4217.           else loc++;
  4218.      }
  4219.     }
  4220.  
  4221. parsing_mode = outer_mode;
  4222. return comment_continues;
  4223. }
  4224.  
  4225. @ Ending the skip over comments is simple:
  4226. @<Finish skipping comment...@>=
  4227. {
  4228. comment_continues = NO; 
  4229. break;
  4230. }
  4231.  
  4232. @* INPUTTING the NEXT TOKEN.
  4233.  
  4234. @<Global...@>=
  4235.  
  4236. EXTERN name_pointer cur_module SET(NULL); /* name of module just scanned */
  4237. EXTERN ASCII c; /* the current character for |get_next| */
  4238. EXTERN boolean strt_cmnt;
  4239. EXTERN boolean strt_point_cmnt;
  4240. EXTERN boolean suppress_newline; // For scanning past invisible comments.
  4241. EXTERN boolean eat_blank_lines; // For `\.{@@\%\%}'.
  4242. EXTERN boolean no_expand SET(NO); // For use with `\.{@@\~}.
  4243.  
  4244. @ As one might expect, |get_next| consists mostly of a big switch that
  4245. branches to the various special cases that can arise.  This function has
  4246. been broken into several function calls in order to fit it into personal
  4247. computers. 
  4248.  
  4249. When we return to token we obtained, we also store it using the |RETURN|
  4250. macro; this sometimes helps us parse the next object.
  4251.  
  4252. @d RETURN(pcode) return (eight_bits)pcode@;
  4253.  
  4254. @<Part 2@>=@[
  4255.  
  4256. eight_bits get_next(VOID) /* produces the next input token */
  4257. {
  4258. GOTO_CODE pcode; // Return code from the parse routines.
  4259.  
  4260. strt_point_cmnt = suppress_newline = NO;
  4261.  
  4262. WHILE()
  4263.     {
  4264.     @<Check if we're at the id part of a preprocessor command@>;
  4265.     @<Check if we're at the end of a preprocessor command@>;
  4266.  
  4267.     if (loc>limit) 
  4268.     if(from_buffer) 
  4269.         {
  4270.         undivert(); /* Stop reading from buffer; go back to
  4271.                 reading from files. */
  4272.         if(stop_the_scan) 
  4273.             return WEB_definition;
  4274.         continue;
  4275.         }
  4276.     else
  4277.         { /* Reading from file. */
  4278.           if (preprocessing && *(limit-1)!=cont_char) 
  4279.             {
  4280.             preprocessing = NO;
  4281.             if(in_cdir)
  4282.                 {
  4283.                 id_first = id_loc = mod_text + 1;
  4284.                 *id_loc++ = cdir;
  4285.                 *id_loc++ = '\0';
  4286.                 in_cdir = NO;
  4287.                 return stringg;
  4288.                 }
  4289.             }
  4290.         if(stop_the_scan) 
  4291.             return WEB_definition;
  4292.           else if(!get_line()) 
  4293.             return new_module;
  4294.  
  4295.         if(eat_blank_lines)
  4296.             {
  4297.             eat_blank_lines = NO;
  4298.  
  4299.             while(loc >= limit)
  4300.                 if(!get_line())
  4301.                     return new_module;
  4302.             }
  4303.  
  4304.         at_beginning = BOOLEAN(!preprocessing);
  4305.  
  4306.           if(prn_where) 
  4307.             {
  4308.                 prn_where=NO;
  4309.  
  4310.                 if(!scanning_defn)
  4311.                 @<Insert the line number into |tok_mem|@>;
  4312.                 }
  4313.             else if(!suppress_newline &&
  4314.             (!R77_or_F || limit==cur_buffer || free_Fortran))
  4315.                  return @'\n';
  4316.         }
  4317.     else at_beginning = BOOLEAN(!preprocessing && (loc == cur_buffer));
  4318.  
  4319. if(preprocessing) 
  4320.     @<Compress string of blanks into one; if any found, return a space@>@;
  4321. else
  4322.     @<Skip white space at beginning of line@>@;
  4323.  
  4324. strt_cmnt = NO;
  4325.  
  4326. switch(language)
  4327.     {
  4328.    case TEX:
  4329.     if(!scanning_defn)
  4330.         {
  4331.         if((pcode=prs_TeX_code()) == MORE_PARSE) 
  4332.             break;
  4333.         else if(pcode < 0) 
  4334.             CONFUSION("prs_TEX_code","Negative pcode");
  4335.         else 
  4336.             RETURN(pcode);
  4337.         }
  4338.  
  4339.    default:
  4340.     if((pcode=prs_regular_code(MORE_PARSE)) == MORE_PARSE) 
  4341.         break;
  4342.     else if((int)pcode < 0)
  4343.         CONFUSION("prs_regular_code","Negative pcode");
  4344.     else 
  4345.         RETURN(pcode);
  4346.     }
  4347.    }
  4348.  
  4349. DUMMY_RETURN(ignore);
  4350. }
  4351.  
  4352. @ Since the preprocessor has different reserved words than C~itself, we
  4353. include the preprocessor token with the identifier if it's first on a
  4354. preprocessor line.
  4355.  
  4356. @<Check if we're at the id...@>=
  4357.  
  4358. if(preprocessing && at_beginning) 
  4359.     {
  4360.     at_beginning = NO;
  4361.  
  4362. /* Preprocessor directives can have white space between the '\.\#' and the
  4363. name. */
  4364.     for( ; loc < limit; loc++)
  4365.         if(!(*loc==@' ' || *loc==tab_mark)) break;
  4366.  
  4367.     *(loc-1) = @'#'; /* Now we're positioned on an identifier beginning
  4368. with |'#'|, with no intervening blanks. */
  4369.     return (eight_bits)prs_regular_code(GOTO_GET_IDENTIFIER);
  4370.     }
  4371.  
  4372. @ When we get to the end of a preprocessor line, we lower the flag and send
  4373. a code \\{right\_preproc}, unless the last character was the continuation
  4374. character'~\.\\'.
  4375.  
  4376. @<Check if we're at the end...@>=
  4377.  
  4378.   if(*loc==cont_char && loc==limit-1 && (preprocessing || free_Fortran))
  4379.     {
  4380.     loc += 2; /* Force it to read another line the next time through. */
  4381.     return (eight_bits)CHOICE(free_Fortran, @'&', cont_char); /* We
  4382. leave the format of the input file alone. Since we're using free-form
  4383. syntax, the compiler will continue the line correctly. */
  4384.     }
  4385.  
  4386. @ Here we are inside a C preprocessing statement.  A run of white space is
  4387. compressed into one blank.
  4388.  
  4389. @<Compress string of blanks...@>=
  4390. {
  4391. boolean found_white_space = NO;
  4392.  
  4393.     do
  4394.         {
  4395.         if((c=*loc++) != @' ' || c != tab_mark) 
  4396.             break;
  4397.  
  4398.         found_white_space = YES;
  4399.         }
  4400.     while(loc < limit);
  4401.  
  4402. @#if(0)
  4403.     if(c==cont_char && loc==limit)
  4404.         if(!get_line()) 
  4405.             return new_module;
  4406.         else 
  4407.             goto compress_blanks;
  4408. @#endif
  4409.         
  4410. if(found_white_space) 
  4411.     return @' ';
  4412. }
  4413.  
  4414. @ Normally, white space at the beginning of line isn't significant---even
  4415. if the line ultimately starts with a preprocessor command.  Two exceptions
  4416. are \TeX\ mode and nuweb mode, since blanks or tabs could be significant
  4417. then.  However, in nuweb mode, white space in front of preprocessor
  4418. commands should be ignored.
  4419.  
  4420. @<Skip white space at beg...@>=
  4421. {
  4422. if(language==TEX) 
  4423.     c = *loc++;
  4424. else 
  4425.     {
  4426.     ASCII HUGE *loc0 = loc; // Remember starting point for nuweb mode.
  4427.  
  4428.     do
  4429.         { /* Skip beginning white space. */
  4430.         c = *loc++;
  4431.         }
  4432.     while(loc<=limit && (c==@' ' || c==tab_mark) );
  4433.  
  4434.     if(nuweb_mode)
  4435.         {
  4436.         if(!(c == @'@@' && *loc == @'#'))
  4437.             { /* Go back to beginning. */
  4438.             loc = loc0;
  4439.             c = *loc++;
  4440.             }
  4441.         }
  4442.     }
  4443. }
  4444.  
  4445. @ Parse \TeX\ code.
  4446. @<Part 2@>=@[
  4447. GOTO_CODE prs_TeX_code(VOID)
  4448. {
  4449. GOTO_CODE icode; // Return code from |get_control_code|.
  4450.  
  4451. if(loc>limit) 
  4452.     return MORE_PARSE;
  4453.  
  4454. if(TeX[c] == TeX_comment) 
  4455.     @<Handle \TeX\ comment@>@;
  4456.  
  4457. if (c==@'@@')
  4458.     {
  4459.     icode = get_control_code();
  4460.  
  4461.     if(icode == MORE_PARSE) 
  4462.         return icode;
  4463.  
  4464.     if((int)(icode) < 0) 
  4465.         return prs_regular_code(icode);
  4466.     else 
  4467.         return (eight_bits)icode;    
  4468.     }
  4469. else 
  4470.     @<Get \TeX\ string@>@;
  4471. }
  4472.  
  4473. @ Generally, comments are retained (|keep_trailing_comments==YES| by
  4474. default) if they don't start a line. 
  4475.  
  4476. @<Handle \TeX\ comment@>=
  4477. {
  4478. long_comment = NO;
  4479.  
  4480. if((all_cmnts_verbatim || (keep_trailing_comments && !at_beginning)) 
  4481.         && !(scanning_defn && is_WEB_macro))
  4482.     {
  4483.     strt_cmnt = YES;
  4484.     }
  4485. else
  4486.     {
  4487.     loc = limit + 1; // Skip rest of line.
  4488.     return MORE_PARSE;
  4489.     }
  4490. }
  4491.  
  4492. @<Get \TeX\ string@>=
  4493. {
  4494. loc--;
  4495. id_first = id_loc = mod_text + 1;
  4496.  
  4497. if(strt_cmnt) 
  4498.     *id_loc++ = begin_Xmeta;
  4499.  
  4500. while(loc < limit)
  4501.     {
  4502.     if(*loc == @'@@')
  4503.         {
  4504.         if(*(loc+1)==@'@@') 
  4505.             *id_loc++ = *loc++;
  4506. @#if 0
  4507.         else break;
  4508. @#endif
  4509.         }
  4510.     else if(!strt_cmnt && TeX[*loc] == TeX_comment && *(loc-1) != @'\\') 
  4511.         break;
  4512.  
  4513.     *id_loc++ = *loc++;
  4514.     }
  4515.  
  4516. if(strt_cmnt) 
  4517.     *id_loc++ = end_Xmeta;
  4518.  
  4519. return stringg;
  4520. }
  4521.  
  4522. @ Parse all languages except \TeX.  Certain parts of this can be called by
  4523. means of the |iswitch| argument.
  4524. @<Part 2@>=@[
  4525. GOTO_CODE prs_regular_code FCN((iswitch))
  4526.     GOTO_CODE iswitch C1("")@;
  4527. {
  4528. GOTO_CODE icode; // Return code from |get_control_code|.
  4529.  
  4530. switch(iswitch)
  4531.     {
  4532.    case MORE_PARSE: break;
  4533.  
  4534.    case GOTO_MISTAKE: goto mistake;
  4535.    case GOTO_GET_IDENTIFIER: goto get_identifier;
  4536.    case GOTO_GET_A_STRING: goto get_a_string;
  4537.    case GOTO_SKIP_A_COMMENT: goto skip_a_comment;
  4538.     }
  4539.  
  4540. if(language != LITERAL)
  4541.     @<Check for ordinary comments@>@;
  4542.  
  4543. /* --- ELLIPSIS --- */
  4544. if(c==@'.' && *loc==@'.' && *(loc+1)==@'.')
  4545.     {
  4546.     ++loc;
  4547.     compress(ellipsis);
  4548.     }
  4549.  
  4550. /* --- DOT CONSTANT: `\.{.FALSE.}' --- */
  4551. else if(FORTRAN_LIKE(language) && dot_constants &&
  4552.         (c == wt_style.dot_delimiter.begin) && !isDigit(*loc))
  4553.     @<Try to identify a dot constant@>@;
  4554.  
  4555. /* --- CONSTANT: `\.{123}', `\.{.1}', or `\.{\\135}' --- */
  4556. else if (isDigit(c) || c==@'\\' || c==@'.') 
  4557.     @<Get a constant@>@;
  4558.  
  4559. /* --- IDENTIFIER --- */
  4560. else if(is_identifier(c)) 
  4561.     @<Get an identifier@>@;
  4562.  
  4563. /* --- STRING --- */
  4564. else if ( (c==@'\'' || c==@'"')
  4565.          || (is_RATFOR_(language) && sharp_include_line==YES && c==@'(') )
  4566.     {
  4567.     if(language == LITERAL)
  4568.         return c;
  4569.     else
  4570.         @<Get a string@>@;
  4571.     }
  4572.  
  4573. /* --- CONTROL CODE --- */
  4574. else if (c==@'@@') 
  4575.     @<Get a control code@>@;
  4576.  
  4577. /* --- WHITE SPACE --- */
  4578. else if (c==@' ' || c==tab_mark) 
  4579.     if(nuweb_mode)
  4580.         return (c==tab_mark ? bell : c);
  4581.     else
  4582.         {
  4583.         if (!preprocessing || loc>limit) return MORE_PARSE;
  4584.           /* we don't want a blank after a final backslash */
  4585.         else return @' '; 
  4586.             // Ignore spaces and tabs, unless preprocessing.
  4587.         }
  4588.  
  4589. /* --- C PREPROCESSOR COMMAND --- */
  4590. else if (c==@'#' && at_beginning && C_LIKE(language)) 
  4591.     {
  4592.     preprocessing = YES;
  4593.     return MORE_PARSE;
  4594.     }
  4595.  
  4596. /* --- END of |@r format| STATEMENT --- */
  4597. else if (in_format && c==@';') /* End a |@r format| statement. */
  4598.     {
  4599.     in_format = NO;
  4600.     return end_format_stmt;
  4601.     }
  4602.  
  4603. /* --- TWO-SYMBOL OPERATOR --- */
  4604. mistake: 
  4605.  if(language != LITERAL)
  4606.     @<Compress two-symbol operator@>@;
  4607.  
  4608. return (eight_bits)c;
  4609. }
  4610.  
  4611.  
  4612. @
  4613. @<Check for ordinary comments@>=
  4614. {
  4615. switch(c)
  4616.     {
  4617.    case (ASCII)begin_comment0:
  4618.     long_comment = strt_cmnt = YES;
  4619.     break;
  4620.  
  4621.    case (ASCII)begin_comment1:
  4622.     strt_cmnt = strt_point_cmnt = YES;
  4623.     long_comment = NO;
  4624.     break;
  4625.  
  4626.    case @'/':
  4627.     if(*loc==@'*')
  4628.         long_comment= strt_cmnt = YES;
  4629.     else if(*loc==@'/' && (C_LIKE(language) || (Cpp_comments &&
  4630. !in_format && FORTRAN_LIKE(language))))
  4631.         { // Short comments are recognized in both~C and \Cpp.
  4632.         long_comment = NO;
  4633.         strt_cmnt = YES;
  4634.         }
  4635.     break;
  4636.  
  4637.    case @'!':
  4638. /* \Fortran\ will handle the commenting style ``\.{! Comment}'' if
  4639. |point_comments| is on, or ``\.{!! Comment}'' always. */
  4640.     if((*loc==@'!' || point_comments) && FORTRAN_LIKE(language))
  4641.         {
  4642.         *(loc-1) = (ASCII)begin_comment1; /* This marker is
  4643. necessary so the verbatim comments don't get confused with \.{@@!}. */
  4644.         strt_cmnt = strt_point_cmnt = YES;
  4645.         long_comment = NO;
  4646.         }
  4647.     break;
  4648.     }
  4649.  
  4650. if(strt_cmnt && all_cmnts_verbatim && !(scanning_defn && is_WEB_macro))
  4651.     {
  4652.     loc--; /* Position on the '\./'. */
  4653.  
  4654.     @<Get a control code@>@;
  4655.     }
  4656. else if(strt_cmnt || comment_continues)
  4657.     {
  4658.     skip_a_comment:
  4659.           skip_comment(); /* scan to end of comment or newline */
  4660.  
  4661.           if ((comment_continues  || !long_comment) && 
  4662.             !(scanning_defn && is_WEB_macro)) return @'\n';
  4663.           else return MORE_PARSE;
  4664.      }
  4665.  
  4666. if(loc==limit && c==cont_char && 
  4667.     (preprocessing || (auto_semi && R77)) ) return MORE_PARSE;
  4668. @#if(0)
  4669.     if(auto_semi && loc==limit && c==cont_char && R77) return MORE_PARSE;
  4670. @#endif
  4671. }
  4672.  
  4673. @ The following code assigns values to the combinations \.{++},
  4674. \.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and
  4675. \.{\&\&}.  The compound assignment operators (e.g., \.{+=}) are 
  4676. separate tokens, according to the \ceeref. (They're not, according to ANSI.
  4677. Pragmatically, there's no more room in the table for more single-byte tokens.)
  4678.  
  4679. @d compress(c) if (loc++<=limit) return (eight_bits)(c)
  4680. @d Fcompress(c) if( is_FORTRAN_(language) && loc < limit) 
  4681.             return (eight_bits)(c) /* Not used. */ 
  4682. @<Compress two...@>=
  4683. switch(c) 
  4684.  {
  4685.   case @'/': 
  4686.   case @'\\':
  4687.     if(FORTRAN_LIKE(language) && !in_format && (*loc == @'/') )
  4688.         {
  4689.         if(Cpp_comments && c==@'/') break; /* In this case, the
  4690. slashes are the \Cpp-style comments.  We'll always allow \.{\\/} as a
  4691. synonym for concatenation. */
  4692.  
  4693.         compress(slash_slash); /* \Fortran's concatenation
  4694. operator. Multiple slashes in |format| statements are just left alone. */
  4695.         }
  4696.     break;
  4697.   case @'+': if (*loc==@'+') compress(plus_plus);  break;
  4698.  
  4699.   case @'-': if (*loc==@'-') {compress(minus_minus);}
  4700.     else if (*loc==@'>') compress(minus_gt);  break;
  4701.  
  4702.   case @'=': if (*loc==@'=') compress(eq_eq);  break;
  4703.  
  4704.   case @'>': if (*loc==@'=') {compress(gt_eq);}
  4705.     else if (*loc==@'>') {compress(gt_gt);}
  4706.     break;
  4707.  
  4708.   case @'<': if (*loc==@'=') {compress(lt_eq);}
  4709.     else if (*loc==@'<') {compress(lt_lt);}
  4710.     else if(*loc==@'>') {compress(not_eq);} /* \FORTRAN-88 */
  4711.     break;
  4712.  
  4713.   case @'&': if (*loc==@'&') compress(and_and);  break;
  4714.  
  4715.   case @'|': if (*loc==@'|') compress(or_or);  break;
  4716.  
  4717.   case @'!': if (*loc==@'=') {compress(not_eq);} break;
  4718.  
  4719.   case @'*': 
  4720.     if(FORTRAN_LIKE(language) && (*loc == @'*') )
  4721.         {compress(star_star);} /* Exponentiation. */
  4722.     break;
  4723.  
  4724.  case @'^': 
  4725.     if(*loc == @'^') {compress(star_star);}
  4726.     else if(FORTRAN_LIKE(language) && (loc < limit) ) return star_star;
  4727.     break; 
  4728.  
  4729.  case @'#': 
  4730.     if(*loc==@'#') {compress(paste);}
  4731.     else if(*loc==@'<') 
  4732.         {
  4733.         loc++;
  4734.         mac_mod_name = YES;
  4735.         @<Scan the module name and make |cur_module| point to it@>; 
  4736.         }
  4737.     break;
  4738.  
  4739.   case @':': if(*loc==@':') compress(colon_colon); @+ break;        
  4740.  
  4741. }
  4742.  
  4743.  
  4744. @ We need a few flags for processing constants.
  4745. @<Glob...@>=
  4746.  
  4747. EXTERN boolean starts_with_0, hex_constant, bin_constant, floating_constant;
  4748.  
  4749. @<Get a constant@>= 
  4750. @{
  4751. boolean decimal_point;
  4752.  
  4753. @b
  4754. if(loc==limit && c==cont_char)
  4755.     {
  4756.     if(preprocessing) loc++;
  4757.     return (eight_bits)c;
  4758.     }
  4759.  
  4760. starts_with_0 = hex_constant = bin_constant = floating_constant = NO;
  4761.  
  4762.   id_first = loc - 1;
  4763.  
  4764.   if (*id_first==@'.' && !isDigit(*loc)) goto mistake; /* not a constant */
  4765.  
  4766.   if (*id_first==@'\\') 
  4767.     {
  4768.     if(*loc == @'/') goto mistake;
  4769.     while (isOdigit(*loc)) loc++; /* octal constant */
  4770.     goto found;
  4771.     }
  4772.   else 
  4773.      {
  4774.      starts_with_0 = BOOLEAN(*id_first==@'0');
  4775.      if (starts_with_0) 
  4776.     {
  4777.     hex_constant = BOOLEAN(*loc==@'x' || *loc==@'X');
  4778.  
  4779.         if (hex_constant) 
  4780.         { /* hex constant---e.g, \.{0xA1} */
  4781.             loc++; while (isXdigit(*loc)) loc++; goto found;
  4782.         }
  4783.     else if( (bin_constant=BOOLEAN(*loc==@'b' || *loc==@'B')) != 0 )
  4784.         { /* Binary constant---e.g., |0b101|. */
  4785.         loc++;
  4786.         while(isBdigit(*loc)) loc++;
  4787.         goto found;
  4788.         }
  4789.     }
  4790.  
  4791. while(isDigit(*loc)) loc++; /* Skip over digits. */
  4792. decimal_point = BOOLEAN(*loc==@'.');
  4793. if(decimal_point) loc++; /* Check if decimal point. */
  4794. while(isDigit(*loc)) loc++; /* Skip over digits after decimal point. */
  4795.  
  4796.     if(FORTRAN_LIKE(language))
  4797.         if(*(loc-1)==@'.')
  4798.             {
  4799. /* If the constant doesn't end with a digit,
  4800. make sure the dot isn't the start of a dot constant. */
  4801.             if(is_dot())
  4802.                 {
  4803.                 loc--;
  4804.                 goto found;
  4805.                 }
  4806.             }
  4807.         else if(*loc == @'h' || *loc == @'H') 
  4808.             @<Get Hollerith string, |goto found|@>@;
  4809.  
  4810.     floating_constant = BOOLEAN(*loc==@'e' || *loc==@'E' ||
  4811.             (FORTRAN_LIKE(language) 
  4812.         && (*loc==@'d' || *loc==@'D' || *loc==@'q' || *loc==@'Q')));
  4813.  
  4814.     if(floating_constant)
  4815.         { /* float constant---e.g., \.{1.0e-5}  */
  4816.         if (*++loc==@'+' || *loc==@'-') loc++;
  4817.             while (isDigit(*loc)) loc++;
  4818.             }
  4819.  
  4820.     floating_constant |= decimal_point;
  4821.   }
  4822.  
  4823.   found: 
  4824.     if (C_LIKE(language))
  4825.         { /* Check for |unsigned|, |long|, or |float| suffix. */
  4826.         boolean its_long = NO, its_unsigned = NO, its_constant = NO;
  4827.  
  4828.         switch(*loc)
  4829.             {
  4830.            case @'l':
  4831.            case @'L':
  4832.             its_constant = its_long = YES;
  4833.             break;
  4834.  
  4835.            case @'u':
  4836.            case @'U':
  4837.             its_constant = its_unsigned = YES;
  4838.             break;
  4839.  
  4840.            case @'f':
  4841.            case @'F':
  4842.             its_constant = YES;
  4843.             break;
  4844.             }
  4845.  
  4846.         if(its_constant)
  4847.             { /* |long|, |float|, or |unsigned|
  4848. constant---e.g., \.{123L} */ 
  4849.             loc++; // Skip over suffix.  
  4850.         
  4851. /* Might be a second suffix. */
  4852.             if(its_long && (*loc == @'u' || *loc == @'U')) 
  4853.                 loc++; // |50LU|
  4854.             else if(its_unsigned && (*loc == @'l' || *loc ==@'L')) 
  4855.                 loc++; // |50UL|
  4856.             }
  4857.         }
  4858.     else if(Fortran88) @<Skip over optional kind parameter@>@;
  4859.  
  4860.   id_loc = loc;
  4861.   return constant;
  4862. }
  4863.  
  4864. @ For \Fortran-90.
  4865. @<Skip over optional kind...@>=
  4866. {
  4867. if(*loc == @'_')
  4868.     while(is_kind(*loc)) loc++;
  4869. }
  4870.  
  4871. @
  4872. @<Get Hollerith string...@>=
  4873. @{
  4874. int l,n;
  4875.  
  4876. @b
  4877. *loc++ = '\0'; /* Terminate string after the length
  4878.     (temporarily overwriting the 'H'); position to actual constant. */
  4879. n = ATOI(id_first); /* Length of constant. */
  4880. *(loc-1) = @'H'; /* Reconstruct the 'H'. */
  4881.  
  4882. for(l = 0; l<n; ++l) ++loc; /* Skip over the constant. */
  4883.  
  4884. goto found;
  4885. }
  4886.  
  4887. @
  4888. @<Try to identify a dot...@>=
  4889. @{
  4890. ASCII HUGE *p0;
  4891. int n;
  4892. eight_bits c;
  4893. ASCII dot_end = wt_style.dot_delimiter.end;
  4894.  
  4895. @b
  4896. /* At this point, |loc| is positioned to the first position after the dot. */
  4897. for(p0=loc, n=0; n<MAX_DOT_LENGTH; n++,loc++)
  4898.     if(*loc == dot_end || !isAlpha(*loc)) break; /* Found end of dot
  4899. constant. */ 
  4900.  
  4901. if(*loc != dot_end) /* Didn't find end. */
  4902.     {
  4903.     loc = p0; /* Reset position back to beginning. */
  4904.     goto mistake;
  4905.     }
  4906.  
  4907. c = dot_code(dots,uppercase(p0,n),loc++,dot_const);
  4908.  
  4909. if(c) return c;
  4910. else
  4911.     {
  4912.     loc = p0;
  4913.     goto mistake;
  4914.     }
  4915.  
  4916. }
  4917.  
  4918. @ Strings and character constants, delimited by double and single
  4919. quotes, respectively, can contain newlines or instances of their own
  4920. delimiters if they are protected by a backslash (for C---e.g., |"ab\"c"|)
  4921. or if they are 
  4922. repeated (for FORTRAN---e.g., |@r 'ab''c'|).  We follow this convention,
  4923. but do not allow the string to be longer than |longest_name|. 
  4924.  
  4925. @<Get a string@>= 
  4926. get_a_string:
  4927. {
  4928.   ASCII delim = c; /* what started the string */
  4929.   ASCII right_delim = c;
  4930.   int level;
  4931. boolean equal_delims;
  4932.  
  4933.   id_first = mod_text+1; /* Position of delimiter. */
  4934.   id_loc = mod_text; *++id_loc=delim;
  4935.  
  4936.   if(delim==@'(')
  4937.     {
  4938.      right_delim = @')'; /* For m4 |@r include|. */
  4939.     sharp_include_line = NO;
  4940.     }
  4941.  
  4942. level = 1;
  4943.  
  4944. equal_delims = BOOLEAN(right_delim==delim);
  4945.  
  4946. WHILE()
  4947. {
  4948.     if (loc>=limit) 
  4949.     {
  4950.       if( (equal_delims || chk_ifelse) && *(limit-1)!=cont_char) 
  4951.             /* Continuation after next line. */ 
  4952.         {
  4953.             err_print(T,"String %s with '%s%c' didn't end",
  4954.             BTRANS, delim==@'\'' ? "\\" : "", XCHR(delim)); 
  4955.         loc=limit; break;
  4956. @.String didn't end@>
  4957.           }
  4958.  
  4959.       if(!get_line()) 
  4960.         {
  4961.             err_print(T,"Input ended in middle of string \
  4962. %s with '%s%c'", BTRANS, delim==@'\'' ? "\\" : "", XCHR(delim)); 
  4963.         loc=cur_buffer; 
  4964.         break; 
  4965. @.Input ended in middle of string@>
  4966.           }
  4967.       else 
  4968.     {
  4969.     if (C_LIKE(language) && ++id_loc<=mod_end) *id_loc = @'\n'; 
  4970. /* More string to come; will print as \.{"\\\\\\n"} */
  4971.     
  4972. /* Now the continuation of the string is in the buffer.  If appropriate,
  4973. skip over beginning white space and backslash. */
  4974.     if(bslash_continued_strings)
  4975.         {
  4976.         for(; loc < limit; loc++)
  4977.             if(*loc != @' ' && *loc != tab_mark) break;
  4978.  
  4979.         if(*loc == cont_char) loc++; /* Move past the backslash. */
  4980.         else err_print(T,"Inserted '%c' at beginning of continued \
  4981. string",XCHR(cont_char));
  4982.         }
  4983.     }
  4984.     }
  4985.  
  4986.     if(!equal_delims) @<Skip over embedded comment@>;
  4987.  
  4988.     if ((c=*loc++)==delim) 
  4989.     {
  4990.     level++;
  4991.  
  4992.       if (++id_loc<=mod_end) *id_loc=c;
  4993.  
  4994.     if(!equal_delims) continue;
  4995.  
  4996.     if( *loc==delim && !(C_LIKE(language) ||
  4997.              (is_RATFOR_(language) && Ratfor77)) ) 
  4998.         ++loc; /* Copy over repeated delimiter. */
  4999.     else   break; /* Found end of string. */
  5000.         }
  5001.  
  5002.     if(c==right_delim)
  5003.         if(--level == 0)
  5004.             {
  5005.           if (++id_loc<=mod_end) *id_loc=c;
  5006.          break; /* Found end of string for unequal delims. */
  5007.             }
  5008.  
  5009. /* Double the quote. */
  5010.     if(R77 && c==@'\'')
  5011.         if(++id_loc <= mod_end) *id_loc = c;
  5012.  
  5013.     if (c==cont_char)
  5014.     {
  5015.       if (loc>=limit && (!is_FORTRAN_(language) || free_form_input)) 
  5016.         continue; /* Continuation of string; throw away the
  5017. continuation character. */
  5018.  
  5019.     if(!is_FORTRAN_(language))
  5020.     {
  5021.       c = *loc++; /* Character after backslash. */
  5022.     
  5023.     if(R77)
  5024.        switch(c)
  5025.         {
  5026. #if(0)
  5027. #define n c
  5028.         @<Convert escape characters@>@;
  5029. #undef n
  5030. #endif
  5031. /* Double the quote for the direct-to-Fortran output. */
  5032.         case @'\'':
  5033.             if(++id_loc <= mod_end) *id_loc = c;
  5034.             break;
  5035.         }
  5036.     else {if (++id_loc<=mod_end) *id_loc = @'\\';}
  5037.     }
  5038.         }
  5039.  
  5040.     if (++id_loc<=mod_end) *id_loc=c; /* Store the character. */
  5041.   }
  5042.  
  5043. found_string:
  5044.   if (id_loc>=mod_end) {
  5045.     SET_COLOR(error);
  5046.     printf("\n! String too long: ");
  5047. @.String too long@>
  5048.     ASCII_write(mod_text+1,25);
  5049.     printf("..."); mark_error;
  5050.   }
  5051.  
  5052.   id_loc++;
  5053.   return stringg;
  5054. }
  5055.  
  5056. @ For parenthesized strings, we shall eat embedded C-style comments.
  5057. @<Skip over embedded...@>=
  5058.  
  5059. if(*loc==@'/' && *(loc+1)==@'*')
  5060.     for(loc += 2; ; loc++)
  5061.         {
  5062.         if(loc >= limit)
  5063.             if(!get_line())
  5064.                 {
  5065.         err_print(T,"Input ended in middle of embedded comment %s",
  5066.             BTRANS);
  5067.                 loc = cur_buffer;
  5068.                 goto found_string;
  5069.                 }
  5070.  
  5071.         if(*loc==@'*' && *(loc+1)==@'/')
  5072.             {
  5073.             loc += 2;
  5074.             break;
  5075.             }
  5076.         }
  5077.  
  5078. @ After an \.{@@}~sign has been scanned, the next character tells us
  5079. whether there is more work to do.
  5080.  
  5081. @<Get a control code@>=
  5082. switch(icode=get_control_code())
  5083.     {
  5084.    case GOTO_MISTAKE: goto mistake;
  5085.    case GOTO_GET_A_STRING: goto get_a_string;
  5086.    case GOTO_GET_IDENTIFIER: goto get_identifier;
  5087.    case GOTO_SKIP_A_COMMENT: goto skip_a_comment;
  5088.  
  5089.    case MORE_PARSE:
  5090.    default: return icode;
  5091.     }
  5092.  
  5093. @
  5094. @<Part 2@>=@[
  5095. GOTO_CODE get_control_code(VOID)
  5096. {
  5097. eight_bits cc; /* The |ccode| value. */
  5098.  
  5099. c = *loc++;
  5100. SET_CASE(c); // Set the |upper_case_code| flag.
  5101.  
  5102. if(c == (ASCII)begin_comment1 || c == (ASCII)begin_comment0)
  5103.     {
  5104.     c = *(loc-1) = @'/'; /* So we can handle this uniformly with C-style
  5105. comments. */
  5106.     strt_cmnt = YES;
  5107.     }
  5108.  
  5109. switch(cc=ccode[c]) 
  5110.     {
  5111.     case ignore: return MORE_PARSE; /* Undefined control code. */
  5112.  
  5113. /* Languages are stored, if necessary, in two parts: |begin_language|, and
  5114. the language itself. Here |set_output_file| sets the language, which can be
  5115. looked at when we're appending. */
  5116.    @<Specific language cases@>:
  5117.     loc--;
  5118.  
  5119.    case L_switch:
  5120.     {
  5121.     @<Set |language|@>@;
  5122.     set_output_file(language);
  5123.     return begin_language;
  5124.     }
  5125.  
  5126.     case control_text: while ((c=skip_ahead(ignore,NO))==@'@@');
  5127.       /* only \.{@@@@} and \.{@@>} are expected */ /* Is |c| used?? */
  5128.  
  5129.       if (*(loc-1)!=@'>') 
  5130.     err_print(T,"Improper @@ within control text %s", BTRANS);
  5131. @.Improper \AT! within control text@>
  5132.  
  5133.       return MORE_PARSE; /* To top of loop in |get_next|. */
  5134.  
  5135.     case module_name: /* \.{@@<} */
  5136.     mac_mod_name = NO; /* Used as a flag for macro processing. */
  5137.     @<Scan the module name and make |cur_module| point to it@>;
  5138.  
  5139.     case stringg: /* \.{@@=} */
  5140.     @<Scan a verbatim string@>;
  5141.  
  5142.     case begin_vcmnt: 
  5143. /* Here the |strt_cmnt| handles all comments verbatim; the last two
  5144. cases handle~\.{@@\slashstar} or~\.{@@//}. */
  5145.     if(strt_cmnt || *loc==@'*' || *loc==@'/')
  5146.         if(!(scanning_defn && is_WEB_macro) && !deferred_macro)  
  5147.             {
  5148.             if(!strt_point_cmnt) long_comment =
  5149.                 BOOLEAN(!(*loc==@'/')); 
  5150.             @<Scan a verbatim comment@>@;  /* \.{@@\slashstar} */ 
  5151.             }
  5152.         else return GOTO_SKIP_A_COMMENT;
  5153.     else return MORE_PARSE; /* The line-break command \.{@@/} is ignored by
  5154. \TANGLE. */
  5155.  
  5156.     case invisible_cmnt:
  5157. /* When we sense an \.{@@\%}, we throw away everything to the end of line,
  5158. including the newline that is normally returned.  */
  5159.     if(*loc == @'%')
  5160.         eat_blank_lines = YES;
  5161.  
  5162.     loc = limit + 1; // Force the next line to be read.
  5163.     suppress_newline = YES;
  5164.     return MORE_PARSE;
  5165.  
  5166.     case compiler_directive:
  5167.         {
  5168.         int n;
  5169.         outer_char *s = t_style.cdir_start[language_num];
  5170.  
  5171.         id_first = id_loc = mod_text + 1;
  5172.  
  5173.         *id_loc++ = cdir;
  5174.  
  5175. /* Starting ``pragma'' string. */
  5176.         STRCPY(id_loc,s);
  5177.         to_ASCII((outer_char HUGE *)id_loc);
  5178.         id_loc += STRLEN(s);
  5179.         
  5180. /* Body. */
  5181.         STRNCPY(id_loc,loc,n = limit - loc);
  5182.         id_loc += n;
  5183.  
  5184.         *id_loc++ = cdir;
  5185.         *id_loc++ = '\0';
  5186.  
  5187.         loc = limit + 1;
  5188.         return stringg;
  5189.         }
  5190.  
  5191.     case Compiler_Directive:
  5192.         {
  5193.         outer_char *s = t_style.cdir_start[language_num];
  5194.  
  5195.         id_first = id_loc = mod_text + 1;
  5196.  
  5197.         *id_loc++ = cdir;
  5198.         preprocessing = in_cdir = YES;
  5199.         at_beginning = NO;
  5200.  
  5201. /* Starting ``pragma'' string. */
  5202.         STRCPY(id_loc,s);
  5203.         to_ASCII((outer_char HUGE *)id_loc);
  5204.         id_loc += STRLEN(s);
  5205.  
  5206.         return stringg;
  5207.         }
  5208.  
  5209.     case new_output_file: // \.{@@o}
  5210.     @<Scan the output file name@>@;
  5211.     loc = limit + 1; // Skip rest of line.
  5212.     return cc;
  5213.  
  5214.     case ascii_constant: /* \.{@@'} or \.{@@"} */
  5215.     if(translate_ASCII) @<Scan an |ASCII| constant@>@;
  5216.     else 
  5217.         {
  5218.         c = *(loc-1); // The starting quote character.
  5219.         return GOTO_GET_A_STRING;
  5220.         }
  5221.  
  5222.     case big_line_break: /* \.{@@\#}. Serves double duty as line break or
  5223. preprocessor command. ??? GENERALIZE??? */
  5224.     if(loc >= limit) return MORE_PARSE;
  5225.  
  5226.     @<Process possible preprocessor command@>; 
  5227.     return MORE_PARSE;
  5228.  
  5229.     case USED_BY_NEITHER:
  5230.     err_print(T,"Invalid `@@%c' ignored",XCHR(c));
  5231.     return ignore;
  5232.  
  5233.     default: return cc;
  5234.   }
  5235. }
  5236.  
  5237. @ Here we copy over the contents of an |ASCII| constant or string.
  5238. @<Scan an |ASCII|...@>= 
  5239. {
  5240. ASCII delim = *(loc-1); // Character that started the string.
  5241.  
  5242.   id_first = loc - 1; // Include the delimiter for later reference.
  5243.  
  5244.   while(*loc != delim)
  5245.     {
  5246.     if (*loc == @'\\') 
  5247.         if(*++loc == delim) 
  5248.             { /* Skip over escape, and possibly escaped
  5249. delimiter. */ 
  5250.             loc++; 
  5251.             continue;
  5252.             }
  5253.  
  5254.     loc++;
  5255.  
  5256.     if (loc>limit) 
  5257.         {
  5258.             err_print(T,"ASCII string %s didn't end", BTRANS); 
  5259.         loc=limit-1; break;
  5260.         }
  5261.     }
  5262.  
  5263.   loc++; // Skip closing delimiter.
  5264.   return ascii_constant;
  5265. }
  5266.       
  5267. @ Process the stuff after~\.{@@<} or~\.{\#<}.
  5268. @<Scan the module name...@>= 
  5269. @{
  5270.   ASCII HUGE *k; /* pointer into |mod_text| */
  5271.   static ASCII ell[] = @"...";
  5272.  
  5273. @b
  5274.   @<Put module name into |mod_text|@>@;
  5275.  
  5276.   if (k-mod_text>3 && STRNCMP(k-2,ell,3)==0) 
  5277.     cur_module=prefix_lookup(mod_text+1,k-3);
  5278.   else cur_module=mod_lookup(mod_text+1,k);
  5279.  
  5280. if(cur_module != NULL)
  5281.     {
  5282.     set_output_file(cur_module->mod_info->language); /* Get current
  5283. language. */ 
  5284.     }
  5285.  
  5286. return module_name;
  5287. }
  5288.  
  5289. @ Module names are placed into the |mod_text| array with consecutive spaces,
  5290. tabs, and carriage-returns replaced by single spaces. There will be no
  5291. spaces at the beginning or the end. (We set |mod_text[0]=' '| to facilitate
  5292. this, since the |mod_lookup| routine uses |mod_text[1]| as the first
  5293. character of the name.)
  5294.  
  5295. @<Set init...@>=
  5296.  
  5297. mod_text[0]=@' ';
  5298.  
  5299. @<Put module name...@>=
  5300. {
  5301. int mlevel = 1; // For nested module names.
  5302.  
  5303. k = mod_text;
  5304.  
  5305. WHILE()
  5306.     {
  5307.     if (loc>limit && !get_line())
  5308.         {
  5309.         err_print(T,"Input ended in section name %s", BTRANS);
  5310. @.Input ended in section name@>
  5311.         loc=cur_buffer+1; break;
  5312.         }
  5313.  
  5314.       c = *loc;
  5315.       @<If end of name, |break|@>;
  5316.       loc++; 
  5317.  
  5318.     if (k<mod_end) k++;
  5319.  
  5320.     switch(c)
  5321.         {
  5322.        case @' ':
  5323.        case tab_mark:
  5324.         c=@' '; if (*(k-1)==@' ') k--; // Compress white space.
  5325.         break;
  5326.  
  5327.        case @';':
  5328.         c = interior_semi;
  5329.         break;
  5330.         }
  5331.  
  5332.     *k = c;
  5333.     }
  5334.  
  5335. if (k>=mod_end) {
  5336.     SET_COLOR(warning);
  5337.   printf("\n! Section name too long: ");
  5338. @.Section name too long@>
  5339.   ASCII_write(mod_text+1,25);
  5340.   printf("..."); mark_harmless;
  5341. }
  5342.  
  5343. if (*k==@' ' && k>mod_text) k--; // Trailing blanks.
  5344. }
  5345.  
  5346. @<If end of name,...@>=
  5347.  
  5348. if (c==@'@@') 
  5349.     {
  5350.     c=*(loc+1);
  5351.  
  5352.     if (c==@'>') 
  5353.         {
  5354.         if(--mlevel == 0)
  5355.             {
  5356.             loc+=2; break; // Successful; position after \.{@@>}.
  5357.             }
  5358.         }
  5359.     else if(c==@'<') mlevel++;
  5360.  
  5361.     if (ccode[c]==new_module) 
  5362.         {
  5363.         err_print(T,"Section name %s didn't end", BTRANS); 
  5364. @.Section name didn't end@>
  5365.         break;
  5366.         }
  5367.  
  5368.     *(++k)=@'@@'; loc++; // Now |c==*loc| again.
  5369.     }
  5370.  
  5371. @ Verbatim comments (C-style comments preceded by~`\.{@@}'), are
  5372. essentially copied intact to the output. Here, we put the comment into the
  5373. |mod_text| buffer; we set |id_first| to the beginning, |id_loc| to the
  5374. end-plus-one, and |loc| to the position after the end-of-comment.
  5375.  
  5376. @<Scan a verbatim comment@>=
  5377. {
  5378. loc--; /* Position to the beginning slash or comment marker (which has been
  5379.         already read as part of~`\.{@@/}'). */
  5380.  
  5381. id_first = id_loc = mod_text + 1; /* A convenient place to put the verbatim
  5382.                     comment. */ 
  5383.  
  5384. if(!C_LIKE(language))
  5385.     {
  5386.     loc++;    /* Skip the opening \.*, for beauty. */
  5387.     @<Make newline and comment character@>;
  5388.     }
  5389.  
  5390. WHILE()
  5391.     {
  5392.     if(loc > limit)
  5393.       if(!long_comment) @<Finish comment and |break|@>@;
  5394.       else if(!get_line())
  5395.         {
  5396.         err_print(T,"Input ended in verbatim comment %s", BTRANS);
  5397. @.Input ended in verbatim comment@>
  5398.         loc = cur_buffer + 1;
  5399.         break;
  5400.         }
  5401.       else 
  5402.         {
  5403.         *id_loc++ = @'\n'; /* Retain line breaks in comments. */
  5404.  
  5405.         if(R66)
  5406.             {
  5407.             *id_loc++ = @'#'; /* Special comment line. */
  5408.             *id_loc++ = @' '; /* Space adds readability. */
  5409.             }
  5410.         }
  5411.  
  5412.     if(id_loc < mod_end - 3) 
  5413.         *id_loc++ = *loc++; /* Copy over the comment. */
  5414.     else
  5415.         {
  5416.         SET_COLOR(warning);
  5417.         printf("\n! Verbatim comment too long: ");
  5418. @.Verbatim comment too long@>
  5419.         ASCII_write(mod_text,25);
  5420.         printf("..."); mark_harmless;
  5421.  
  5422.         id_loc = mod_end - 3;
  5423.         *id_loc++ = @'*'; *id_loc++ = @'/'; /* Terminate the comment
  5424. (prematurely). */
  5425.         comment_continues = YES; /* This is so |get_next| can skip
  5426. the remainder of the comment. */
  5427.         goto finish_vcmnt;
  5428.         }
  5429.  
  5430. /* Check for end of verbatim comment. */
  5431.     if(long_comment && *loc == @'/' && *(loc-1)==@'*')
  5432.         {
  5433.         *id_loc++ = *loc++; /* Position after end of comment. */
  5434.         @<Finish comment and |break|@>@;
  5435.         }
  5436.     }
  5437.  
  5438. finish_vcmnt:
  5439.     if(!C_LIKE(language))
  5440.         {
  5441.         *id_loc++ = '\0';
  5442.         }
  5443.     return stringg; /* Complete comment copied. */
  5444. }
  5445.  
  5446.  
  5447. @
  5448. @<Finish comment...@>=
  5449. {
  5450. if(C_LIKE(language))
  5451.     { /* If we're not using \Cpp, we'll change short comments back to
  5452. standard form so they can be understood by the compiler. */
  5453.     if(!long_comment && !Cpp)
  5454.         {
  5455.         *id_loc++ = id_first[1] = @'*';
  5456.         *id_loc++ = id_first[0] = @'/';
  5457.         }
  5458.     }
  5459. else
  5460.     { /* In \Fortran, kill off the trailing terminator. */ 
  5461.     if(long_comment) id_loc -= 2; 
  5462.     }
  5463.  
  5464. break;
  5465. }
  5466.  
  5467. @ Verbatim comments not in~C must start on a new line, and must be prefixed
  5468. with a comment character.
  5469. @<Make newline and c...@>=
  5470. {
  5471. if(R66) *id_loc++ = @'#';
  5472. else *id_loc++ = @'\n';
  5473.  
  5474. }
  5475.  
  5476. @ At the present point in the program we have |*(loc-1)=stringg|; we set
  5477. |id_first| to the beginning of the string itself, and |id_loc| to its
  5478. ending-plus-one location in the buffer.  We also set |loc| to the position
  5479. just after the ending delimiter.
  5480.  
  5481. @<Scan a verbatim string@>= 
  5482. {
  5483. id_first = loc; /* This used to be |loc++|, but that doesn't handle null
  5484.         string correctly. */
  5485.  
  5486. *(limit+1) = @'@@'; *(limit+2) = @'>'; // Delimiters for verbatim string.
  5487.  
  5488. while (*loc != @'@@' || *(loc+1) != @'>') 
  5489.     loc++; // Verbatim string must end on same line.
  5490.  
  5491. if (loc >= limit) err_print(T,"Verbatim string %s didn't end", BTRANS);
  5492. @.Verbatim string didn't end@>
  5493.  
  5494. id_loc = loc; 
  5495. loc += 2; // Just after \.{@@>}.
  5496. return stringg;
  5497. }
  5498.  
  5499. @* GENERATING REPLACEMENT TEXTS.  The rules for generating the replacement
  5500. texts corresponding to macros and \cee\ texts of a module are almost
  5501. identical; the only differences are that
  5502.  
  5503. {\narrower
  5504. \yskip \item{a)}Module names are not allowed in macros;
  5505. in fact, the appearance of a module name terminates such macros and denotes
  5506. the name of the current module.
  5507.  
  5508. \item{b)}The symbols \.{@@d}, \.{@@f}, and \.{@@a} are not allowed after
  5509. module names, while they terminate macro definitions.
  5510.  
  5511. }
  5512.  
  5513. \yskip Therefore there is a single procedure |scan_repl| whose parameter
  5514. |t| specifies either |macro| or |module_name|. After |scan_repl| has acted,
  5515. |cur_text| will point to the replacement text just generated, and
  5516. |next_control| will contain the control code that terminated the activity.
  5517.  
  5518. /* In certain contexts, it is required to stop the scan at the end of the
  5519. current line. */
  5520. @d STOP (boolean)YES
  5521. @d DONT_STOP (boolean)NO
  5522.  
  5523. /* Add a token to |token_mem|. */
  5524. @d app_repl(c)  {if (tok_ptr==tok_m_end) 
  5525.                 OVERFLW("tokens",ABBREV(max_toks_t));
  5526.             *tok_ptr++= (eight_bits)(c);} 
  5527.  
  5528. @<Global...@>=
  5529.  
  5530. EXTERN text_pointer cur_text; // Replacement text just formed by |scan_repl|.
  5531.  
  5532. EXTERN eight_bits next_control;
  5533.  
  5534. @ Creates a replacement text.
  5535. @<Part 3@>=@[
  5536.  
  5537. SRTN scan_repl FCN((t,stop))
  5538.     eight_bits t C0("Either |macro| or |module_name|.")@;
  5539.     boolean stop C1("IF |YES|, stops the scan at the end of current\
  5540. line.")@; 
  5541. {
  5542. eight_bits a0 = ignore;  /* the current token */
  5543. sixteen_bits a; /* An identifier number. */
  5544. LANGUAGE language0;
  5545. int ntoken = 2;
  5546. boolean auto_bp = YES; /* Breakpoints are inserted automatically, unless
  5547. the module starts off with \.{@@\lb}. */
  5548. boolean scanning_meta = NO;
  5549.  
  5550. language0 = language; /* Save incoming language, in case while we're
  5551.             reading ahead we change it. */
  5552. stop_the_scan = stop;
  5553.  
  5554. if (t==module_name) 
  5555.     @<Insert the line number into |tok_mem|@>;
  5556. else if(stop) 
  5557.     @<Stop scan@>@;
  5558.  
  5559. WHILE()
  5560.     {
  5561.     if(stop)
  5562.         {
  5563.         while(loc <= limit) 
  5564.             if(*loc != @' ') break;
  5565.             else loc++;
  5566.  
  5567.         if(loc > limit) goto done;
  5568.         }
  5569.  
  5570. /* The |ntoken| counter starts out at~2. It is used to see whether the
  5571. first thing in the module is a left brace. If so, the |_BP| macro is
  5572. inserted after the brace for debugging purposes. */
  5573.     if(ntoken) 
  5574.         ntoken--;
  5575.  
  5576.     a0 = (ntoken && nuweb_mode && t==module_name) 
  5577.         ? begin_meta : get_next(); // !!!!!
  5578.  
  5579. reswitch:
  5580.     switch(a0)
  5581.         {
  5582.        case @'\\':
  5583.          if(loc==limit && language!=LITERAL)
  5584.             {
  5585.             if(!get_line())
  5586.                 FATAL("!! Input ended while scanning \
  5587. WEB preprocessor statement","");
  5588.             @<Stop scan@>@;
  5589.             }
  5590.         else 
  5591.             {
  5592.             app_repl(a0);
  5593.  
  5594.             if(language == LITERAL) 
  5595.                 loc++;
  5596.             }
  5597.         break;
  5598.  
  5599.        case @'#': 
  5600.         if(t==macro && is_WEB_macro) 
  5601.             @<Possibly insert statement number@>@;
  5602.         else 
  5603.             {
  5604.             app_repl(a0);
  5605.             }
  5606.         break;
  5607.  
  5608.           @<In cases that |a0| is a non-ASCII token (|identifier|,
  5609.         |module_name|, etc.), either process it and change |a0| to a byte
  5610.         that should be stored, or |continue| if |a0| should be ignored,
  5611.         or |goto done| if |a0| signals the end of this replacement text@>@;
  5612.  
  5613. @#if(0)
  5614.     case @'\n':
  5615.         if(is_WEB_macro) continue;
  5616. @#endif
  5617.  
  5618.        case @'\n':
  5619. /* As far as checking whether a left brace begins a module, we don't care
  5620. about newlines. */
  5621.         if(ntoken) ntoken++;
  5622.         app_repl(a0);
  5623.         break;
  5624.  
  5625.        case @'{':
  5626.         app_repl(a0);
  5627.  
  5628.         if(ntoken && breakpoints && t==module_name&&auto_bp) 
  5629.             @<Insert the |_BP| macro for debugging@>@;
  5630.  
  5631.         break;
  5632.  
  5633.        case begin_bp:
  5634.         auto_bp = NO; // A manual insertion command is coming up.
  5635.         app_repl(@'{');
  5636.         break;
  5637.  
  5638.        case insert_bp:
  5639.         if(breakpoints) 
  5640.             @<Insert the |_BP|...@>@;
  5641.         break;
  5642.  
  5643.        default: 
  5644.         app_repl(a0); // Store |a0| in |tok_mem|.
  5645.         break;
  5646.         }
  5647.     }
  5648.  
  5649.   done: 
  5650.     if(stop_the_scan && !from_buffer)
  5651.         {
  5652.         stop_the_scan = NO;
  5653.         next_control = ignore;
  5654.         }
  5655.     else next_control = 
  5656.         (eight_bits)CHOICE((from_buffer && loc > limit) || stop,
  5657.                 ignore, a0);
  5658.  
  5659. @<Make |cur_text = text_ptr|; update |text_ptr|@>;
  5660. cur_text->Language = (boolean)language0; // Use the starting language.
  5661. }
  5662.  
  5663. @ For modules that start with a left brace, if the |_BP| macro has been
  5664. defined and/or we're in the debugging mode, then while we're reading things
  5665. in we insert a call to that macro, with arguments the module number and
  5666. module name. We build the call into the temporary buffer |bp_cmd|, then
  5667. divert the input stream into that buffer.
  5668.  
  5669. @d BP_BUF_SIZE (13 + MAX_ID_LENGTH) /* The print command below generates a
  5670.     string of the form ``\.{\_BP(99999,"\dots")}'', where the \dots\
  5671.     correspond to |name_of|, whose maximum length is |MAX_ID_LENGTH|. */
  5672.  
  5673. @<Insert the |_BP|...@>=
  5674. {
  5675. ASCII bp_cmd[BP_BUF_SIZE];
  5676.  
  5677. if(cur_module != NULL)
  5678.     {
  5679.     SPRINTF(BP_BUF_SIZE,bp_cmd,`"_BP(%d,\"%s\")",
  5680.         module_count,name_of((sixteen_bits)(cur_module-name_dir))`);
  5681.     to_ASCII(OC(bp_cmd));
  5682.     divert(bp_cmd,bp_cmd+STRLEN(bp_cmd),DONT_STOP);
  5683.     }
  5684. }
  5685.  
  5686. @ If the user has defined the macro |_BP| from the command line, then we
  5687. turn on the |breakpoints| flag so the macro can be inserted in front of
  5688. every module beginning with a left brace.
  5689. @<Glob...@>=
  5690.  
  5691. EXTERN boolean breakpoints;
  5692.  
  5693. @<Has the |_BP| macro been defined?@>=
  5694. @{
  5695. IN_COMMON ASCII HUGE *pbp;
  5696.  
  5697. @b
  5698. breakpoints = BOOLEAN(MAC_LOOKUP(ID_NUM(pbp,pbp+3)) != NULL);
  5699. }
  5700.  
  5701. @ (Sometimes used during debugging.)
  5702. @<Define internal...@>=
  5703.  
  5704. @#if(0)
  5705.     SAVE_MACRO("_BP(m,name)");
  5706. @#endif
  5707.  
  5708. @
  5709. @<Make |cur_text...@>=
  5710. {
  5711. if (text_ptr>text_end) OVERFLW("texts",ABBREV(max_texts));
  5712. cur_text = text_ptr; 
  5713. (++text_ptr)->tok_start = tok_ptr; /* The next start is the present end. */
  5714. }
  5715.  
  5716. @ Prevent macro scan for \.{@@\#if(...)} from overrunning end of line, by
  5717. inserting a |WEB_definition| command at the end.
  5718. @<Stop scan@>=
  5719. {
  5720. *limit = @' ';
  5721. *(limit+1) = @'@@';
  5722. *(limit+2) = @'m';
  5723. }
  5724.  
  5725. @ For inserting the line number, we use a function call to keep the code small.
  5726. @<Insert the line number into |tok_mem|@>=ins_ln_no()@;
  5727.  
  5728. @ Here is the code for the line number: first a |sixteen_bits| equal to
  5729. $|0150000| \equiv |LINE_NUM|$; then, if we're dealing with the change file,
  5730. the line number plus |0100000|; or, if we're dealing with the web file, the
  5731. line number; or, if we're dealing with an include file, the number 0, then
  5732. the line number, followed by the number of characters in the file name and
  5733. the file name.
  5734.  
  5735. @<Part 3@>=
  5736. SRTN ins_ln_no(VOID)
  5737. {
  5738. name_pointer np;
  5739.  
  5740. store_two_bytes((sixteen_bits)LINE_NUM); // $\equiv$ a mod.\ \# of~0.
  5741.  
  5742. if(line_info)
  5743.     {
  5744.     id_first = x_to_ASCII(changing ? change_file_name : cur_file_name);
  5745.     id_loc = id_first + STRLEN(id_first);
  5746.  
  5747.     store_two_bytes((sixteen_bits)(changing ? change_line : cur_line));
  5748.  
  5749.     store_two_bytes(ID_NUM_ptr(np,id_first,id_loc));
  5750.     np->Language = (boolean)NO_LANGUAGE; // \bfit Is this used???
  5751.     }
  5752. }
  5753.  
  5754. @ This fragment stores away an identifier token returned from |id_lookup|.
  5755. @<Append identifier token@>=
  5756. @{
  5757. app_repl(LEFT(a,ID0));
  5758. app_repl(RIGHT(a));
  5759. }
  5760.  
  5761. @
  5762. @<Get and append an identifier token@>=
  5763.  
  5764. a = ID_NUM(id_first,id_loc);
  5765. @<Append identifier token@>@;
  5766.  
  5767. @<In cases that |a0| is...@>=
  5768.  
  5769. case identifier: 
  5770.     @<Get and append an identifier token@>@;
  5771.     break;
  5772.  
  5773. case module_name:
  5774. /* In a macro, the appearance of a module name beginning with
  5775.     \.{@@<} ends the macro and the definition section. On the other
  5776. hand, the construction \.{\#<\dots@@>} is OK in a macro. */
  5777.  if (t==macro && !mac_mod_name) 
  5778.     goto done;
  5779.  else 
  5780.   {
  5781.     @<Was an '@@' missed here?@>;
  5782.     a = cur_module - name_dir;
  5783.     app_repl(LEFT(a,MOD0));
  5784.     app_repl(RIGHT(a));
  5785.     @<Insert the line number into |tok_mem|@>; 
  5786.     if(nuweb_mode)
  5787.     { /* !!!!! */
  5788.     a0 = begin_meta;
  5789.     goto reswitch;
  5790.     }
  5791.     break;
  5792.   }
  5793.  
  5794. case constant: 
  5795. case stringg:
  5796.   @<Copy a string or verbatim construction or numerical constant@>;
  5797.  
  5798. case ascii_constant:
  5799.   cp_ASCII();
  5800.   break;
  5801.  
  5802. case begin_meta:
  5803.     @<Process |begin_meta|@>@;
  5804.     break;
  5805.  
  5806. case end_meta:
  5807.     @<Start column mode.@>;
  5808.     get_line();
  5809.     app_repl(end_meta);
  5810. @%    app_repl('\0');
  5811.     app_repl(stringg);
  5812.     scanning_meta = NO;
  5813.     break;
  5814.  
  5815. case dot_const:
  5816.     app_repl(a0);
  5817.     app_repl(dot_op.num); // |dot_op| was filled by |dot_code|.
  5818.     break;
  5819.  
  5820. case begin_language:
  5821.     switch(language)
  5822.         {
  5823.        case NO_LANGUAGE:
  5824.         CONFUSION("scan_repl:begin_language","Language isn't defined");
  5825.  
  5826.        case RATFOR:
  5827.        case RATFOR_90:
  5828.         if(!RAT_OK("(scan_repl)")) 
  5829.             CONFUSION("scan_repl:begin_language",
  5830.                 "Attempting to append @@Lr");
  5831.  
  5832.        case C:
  5833.        case C_PLUS_PLUS:
  5834.        case LITERAL:
  5835.         column_mode = NO;
  5836.         break;
  5837.  
  5838.        case FORTRAN:
  5839.        case FORTRAN_90:
  5840.        case TEX:
  5841.         if(!(scanning_defn || free_form_input)) 
  5842.             @<Set up column mode@>@;
  5843.         break;
  5844.  
  5845.        default:
  5846.         CONFUSION("app_id","Invalid language");
  5847.         }
  5848.  
  5849. /* We append the language in two parts: |begin_language|, and the language
  5850. itself. This is so we didn't have to tie up many non-printable |ASCII|
  5851. tokens. See the inverse code in |get_output|. */
  5852.     set_output_file(language);
  5853.     if(!scanning_defn) 
  5854.         {app_repl(a0);app_repl((eight_bits)language);}
  5855.     @<Insert the module number into |tok_mem|@>@;
  5856.     @<Insert the line number...@>;
  5857.     break;
  5858.  
  5859. case no_mac_expand:
  5860.     app_repl(begin_language);
  5861.     app_repl(a0);
  5862.     break;
  5863.  
  5864. case new_output_file:
  5865.     if(t == macro) 
  5866.         goto done;
  5867.     else
  5868.         {
  5869.         name_pointer np;
  5870.  
  5871.         app_repl(begin_language); // We piggy-back on |begin_language|.
  5872.         app_repl(NO_LANGUAGE);
  5873.         app_repl(upper_case_code); /* Scope of file name:
  5874. \.{@@o}~means local; \.{@@O}~means global. */
  5875.         a = ID_NUM_ptr(np, id_first, id_loc);
  5876.         @<Append identifier token@>@;
  5877.         np->macro_type = FILE_NAME; // To prevent truncations.
  5878.  
  5879.         if(nuweb_mode)
  5880.             {
  5881.             a0 = begin_meta;
  5882.             goto reswitch;
  5883.             }
  5884.         }
  5885.     break;
  5886.  
  5887. case WEB_definition:
  5888.     if(t == macro) 
  5889.         goto done;
  5890.     else 
  5891.         {
  5892.         @<Append a deferred macro@>;
  5893.         continue;
  5894.         }
  5895.  
  5896. case begin_nuweb:
  5897.     if(t != module_name)
  5898.         {
  5899.         nuweb_mode1 = !NUWEB_MODE;
  5900.         goto done;
  5901.         }
  5902.     else
  5903.         {
  5904.     ERR_PRINT(W,"@@N ignored; must appear before beginning of code part");
  5905.         continue;
  5906.         }
  5907.  
  5908. case formatt: 
  5909. case limbo_text: case op_def: case macro_def:
  5910. case definition: case undefinition:
  5911. case begin_code: 
  5912.   if (t!=module_name) 
  5913.     goto done;
  5914.   else 
  5915.     {
  5916.     ERR_PRINT(T,"@@d, @@l, @@v, @@w, @@u, @@f, and @@a \
  5917. are ignored in code text"); 
  5918.     continue; 
  5919. @.\AT!d, \AT!f and \AT!c are ignored in code text@>
  5920.     }
  5921.  
  5922. case end_of_buffer: 
  5923.     a0 = ignore;
  5924.  
  5925. case m_ifdef: case m_ifndef:
  5926. case m_if: case m_else: case m_elif: case m_endif: case m_undef: case m_pragma:
  5927. case m_for: case m_endfor:
  5928. case new_module: 
  5929.     goto done;
  5930.  
  5931. @
  5932. @<Process |begin_meta|@>=
  5933. {
  5934. app_repl(stringg);
  5935. app_repl(a0); /* |begin_meta| inside strings means to insert the
  5936.         |meta| stuff from the style file. */
  5937.  
  5938. if(FORTRAN_LIKE(language)) 
  5939.     {
  5940.     column_mode = NO;
  5941.     app_repl(@'\n');
  5942.     }
  5943.  
  5944. scanning_meta = YES;
  5945.  
  5946. }
  5947.  
  5948. @
  5949. @<Unused@>=
  5950.     WHILE()
  5951.         {
  5952.         if(loc >= limit) // !!!!!!
  5953.             if(!get_line())
  5954.                 {
  5955.                 if(!nuweb_mode)
  5956.         err_print(T,"Input ended during meta-comment %s", BTRANS); 
  5957.                 break;
  5958.                 }
  5959.         
  5960.         while(loc < limit)
  5961.             {
  5962.             if(*loc == @'@@')
  5963.                 @<Check for end of meta-comment and |goto
  5964. done_meta| if necessary@>@; 
  5965.  
  5966.             if(is_identifier(*loc))
  5967.                 @<Append a meta-identifier@>@;
  5968.             else
  5969.                 app_repl(*loc++);
  5970.             }
  5971.         
  5972.         app_repl(@'\n');
  5973.         }
  5974.     
  5975.  
  5976. @
  5977. @<Append a meta-id...@>=
  5978. {
  5979. loc++;
  5980. @<Make |id_first|...@>@;
  5981. @<Get and append an identifier token@>@;
  5982. }
  5983.  
  5984. @
  5985. @<Check for end of meta-comment ...@>=
  5986. {
  5987. switch(ccode[*(loc+1)])
  5988.     {
  5989.    case ignore:
  5990.    case @'b':
  5991.    case @'{':
  5992.     if(nuweb_mode) loc += 2;
  5993.     break;
  5994.  
  5995.    case end_meta:
  5996.     @<Start column mode.@>;
  5997.     get_line();
  5998.     goto done_meta;
  5999.  
  6000.    case new_module:
  6001.     goto done_meta; // !!!!!
  6002.  
  6003.    case @'@@':
  6004.     loc++;
  6005.     break;
  6006.  
  6007.    case invisible_cmnt:
  6008.     if(*(loc+2) == @'%')
  6009.         eat_blank_lines = YES;
  6010.  
  6011.     get_line();
  6012.  
  6013.     if(eat_blank_lines)
  6014.         {
  6015.         eat_blank_lines = NO;
  6016.  
  6017.         while(loc >= limit)
  6018.             if(!get_line())
  6019.                 goto done_meta;
  6020.         }
  6021.  
  6022.     continue;
  6023.     
  6024.    default:
  6025.     if(nuweb_mode)
  6026.         goto done_meta;  // !!!!!
  6027.  
  6028.       break;
  6029.     }
  6030. }
  6031.  
  6032. @ When |WEB_definition| is encountered in the code section, it signifies a
  6033. deferred macro. This has to be put into the special, deferred pool, not
  6034. into the current text being created.
  6035. @<Glob...@>=
  6036.  
  6037. EXTERN int n_unique SET(0);
  6038. EXTERN boolean deferred_macro SET(NO);
  6039.  
  6040. @ The deferred macro is referenced from the current text by creating a
  6041. special identifier of the form \.{@@}|n_unique|\.{name}, where |n_unique|
  6042. is incremented for each new reference to a deferred macro. The |equiv|
  6043. field in this identifier points to the deferred pool.
  6044.  
  6045. We must do some annoying copying in order to use the same routine
  6046. |app_macro|. This could be prettied up.
  6047. @<Append a deferred macro@>=
  6048. {
  6049. #define NAME_LEN 100
  6050.  
  6051. name_pointer np;
  6052. eight_bits HUGE *tok_ptr0, HUGE *tok_m_end0;
  6053. text_pointer text_ptr0,text_end0;
  6054. outer_char new_name[NAME_LEN];
  6055. ASCII HUGE *nn, HUGE *b;
  6056. sixteen_bits a;
  6057.  
  6058. if(!deferred_macros)
  6059.     {
  6060.     ERR_PRINT(T,"Sorry, deferred WEB macros (defined in code part) are \
  6061. prohibited; use option `-TD' to permit them");
  6062.     continue;
  6063.     }
  6064.  
  6065. tok_ptr0 = tok_ptr;
  6066. tok_m_end0 = tok_m_end;
  6067. text_ptr0 = text_ptr;
  6068. text_end0 = text_end;
  6069.  
  6070. tok_ptr = tok_dptr;
  6071. tok_m_end = tokd_end;
  6072. text_ptr = txt_dptr;
  6073. text_end = textd_end;
  6074.  
  6075. deferred_macro = YES;
  6076. np = app_macro(WEB_definition);
  6077. deferred_macro = NO;
  6078.  
  6079. tok_dptr = tok_ptr;
  6080. tok_ptr = tok_ptr0;
  6081. tok_m_end = tok_m_end0;
  6082.  
  6083. txt_dptr = text_ptr;
  6084. text_ptr = text_ptr0;
  6085. text_end = text_end0;
  6086.  
  6087. if(np == NULL) continue;
  6088.  
  6089. /* Create a unique name, beginning with '@@'. */
  6090. SPRINTF(NAME_LEN,new_name,`"@@%d",n_unique++`);
  6091. to_ASCII(new_name);
  6092. for(nn=(ASCII *)new_name+STRLEN(new_name),b=np->byte_start; 
  6093.         b<(np+1)->byte_start; )
  6094.     *nn++ = *b++;
  6095.  
  6096. a = ID_NUM_ptr(np,(ASCII *)new_name,nn);
  6097. @<Append identifier token@>;
  6098.  
  6099. np->macro_type = DEFERRED_MACRO;
  6100. np->equiv = (EQUIV)cur_text;
  6101.  
  6102. #undef NAME_LEN
  6103. }
  6104.  
  6105. @ Here we handle the cases in which `\.{\#}'~is expanded on \It{input}.
  6106. `\.{\#:0}'~expands into a unique statement number.
  6107. `\.{\#!}'~followed by a macro token means copy the definition of that
  6108. macro, but don't expand it. `\.{\#}'~followed by a macro token means
  6109. substitute the complete expansion of that macro.
  6110.  
  6111. @<Possibly insert statement...@>=
  6112. {
  6113. switch(*loc)
  6114.     {
  6115.    case @':':
  6116.     @<Possibly insert a unique statement label@>@; @+ break;
  6117.  
  6118.    case @'!':
  6119.     if(scanning_defn) @<Copy but don't expand macro@>@; 
  6120.     else app_repl(@'#');
  6121.     break;
  6122.  
  6123.    case @'\'':
  6124.    case @'"':
  6125.     app_repl(a0);
  6126.     app_repl(*loc++);
  6127.     break;
  6128.  
  6129.    default:
  6130.     @<Try to expand macro after \.{\#}'@>@; 
  6131.     break;
  6132.     }
  6133. }
  6134.  
  6135. @
  6136. @d N_IDBUF 100
  6137. @<Possibly insert a unique statement...@>=
  6138. @{
  6139. outer_char temp[N_IDBUF];
  6140. ASCII HUGE *t;
  6141.  
  6142. @b
  6143. loc++; /* Move past the colon. */
  6144.  
  6145. /* Check if it's '\.0'---immediate statement number. If not, pass it
  6146. through to the output phase. */
  6147. if(*loc != @'0')
  6148.     {
  6149.     app_repl(@'#');
  6150.     app_repl(@':');
  6151.     break;
  6152.     }
  6153.  
  6154. loc++; /* Move past the zero.*/
  6155. SPRINTF(N_IDBUF,temp,`"%lu",max_stmt++`); /* Make the number. */
  6156. to_ASCII(temp);
  6157.  
  6158. /* Append the number, bracketed by |constant|. */
  6159. app_repl(constant);
  6160.     
  6161. for(t=(ASCII *)temp; *t != '\0'; t++) app_repl(*t);
  6162.  
  6163. app_repl(constant);
  6164. }
  6165.  
  6166. @ We get to here when on input `\.{\#}' is not followed by `\.!' or `\.:'.
  6167. @<Try to expand macro after...@>=
  6168. @{
  6169. sixteen_bits a;
  6170.  
  6171. @b
  6172. if(isDigit(*loc) || *loc==@',' || *loc==@'&' || *loc==@'*' || *loc==@'.' ||
  6173.         *loc==@'[' || *loc==@'{') 
  6174. /* It's one of the forms `\.{\#}$nnn$', `\.{\#,}', `\.{\#\&}', `\.{\#*}',
  6175. or `\.{\#.}'; these are processed on output. */  
  6176.     {app_repl(@'#');} 
  6177. else if(get_next() != identifier) 
  6178.     MACRO_ERR("! '#' should be followed by identifier",YES);
  6179. else
  6180.     {
  6181.     a = ID_NUM(id_first,id_loc);
  6182.  
  6183. /* Check to see if the identifier is an already-defined macro; if not, it's
  6184. the stringizing operation, which is processed on output; just
  6185. append the identifier. */
  6186.     if( (MAC_LOOKUP(a)) == NULL)
  6187.         {
  6188.         app_repl(@'#');
  6189.         @<Append identifier token@>;
  6190.         break;
  6191.         }
  6192.  
  6193. /* Asking for immediate expansion of macro. */
  6194.     MACRO_ERR("! Immediate expansion of macro \"%s\" not implemented",
  6195.         YES,name_of(a));
  6196.     @<Append identifier token@>;
  6197.     }
  6198. }
  6199.  
  6200. @ If the construction `\.{\#!}' is followed by a macro id (without
  6201. arguments), then the token definition of that macro is substituted, without
  6202. expansion. 
  6203. @<Copy but don't expand macro@>=
  6204. @{
  6205. sixteen_bits a;
  6206.  
  6207. @b
  6208. loc++; /* Position to after `\.!'. */
  6209.  
  6210. if(get_next() != identifier) 
  6211.     ERR_PRINT(M,"Identifier must follow #!; command ignored");
  6212. else
  6213.     {
  6214.     text_pointer m;
  6215.  
  6216.  /* Add the identifier to the table if necessary. */
  6217.     a = ID_NUM(id_first,id_loc);
  6218.  
  6219. /* If it's an identifier but not a macro, it must be the construction
  6220. `\.{\#!}|arg|'; just append that for later processing. */
  6221.     if( (m=MAC_LOOKUP(a)) == NULL) 
  6222.         {
  6223.         app_repl(@'#');
  6224.         app_repl(@'!');
  6225.         @<Append identifier token@>;
  6226.         break;
  6227.         }
  6228.     else
  6229.         if(m->nargs > 0) 
  6230.             ERR_PRINT(M,"Macro after #! may not have arguments");
  6231.         else @<Copy tokens of macro@>@;
  6232.     }
  6233. }
  6234.  
  6235. @ Here we append the tokens of a macro definition, without expanding them.
  6236. @<Copy tokens of macro@>=
  6237. @{
  6238. eight_bits HUGE *q0, HUGE *q1;
  6239.  
  6240. @b
  6241. q0 = m->tok_start + m->moffset;
  6242. q1 = (m+1)->tok_start;
  6243.  
  6244. /* Just copy the definition without expanding. */
  6245. while(q0 < q1) app_repl(*q0++);
  6246. }
  6247.  
  6248. @<Was an '@@'...@>= 
  6249. @{
  6250.   ASCII HUGE *try_loc=loc;
  6251.  
  6252. @b
  6253.   while (*try_loc==@' ' && try_loc<limit) try_loc++;
  6254.   if (*try_loc==@'+' && try_loc<limit) try_loc++;
  6255.   while (*try_loc==@' ' && try_loc<limit) try_loc++;
  6256.   if (*try_loc==@'=') 
  6257.     ERR_PRINT(T,"Nested named modules.  Missing `@@*' or `@@ '?");
  6258. @.Nested named modules@>
  6259. }
  6260.  
  6261. @ We will {\it bracket} the string or constant with the id token.
  6262. @<Copy a string...@>=
  6263.  
  6264. if(C_LIKE(language))
  6265.     if(bin_constant && a0==constant) @<Convert binary constant@>@;
  6266.     else copy_string(a0);
  6267. else if(a0 == constant)
  6268.     if(hex_constant) @<Convert hex constant@>@;
  6269.     else if(bin_constant) @<Convert binary constant@>@;
  6270.     else if(starts_with_0 && !floating_constant) @<Convert octal
  6271. constant@>@; 
  6272.     else copy_string(a0);
  6273. else if(R77 && a0==stringg && !in_format)
  6274.     if(*id_first==@'\'') rdc_char_constant();
  6275.     else
  6276.         {
  6277. /* Replace the Ratfor double quote with Fortran's single quote. Watch out
  6278. for a verbatim comment that doesn't start with quote at all. */
  6279.         if(*id_first == @'"') *id_first = *(id_loc-1) = @'\'';
  6280.         copy_string(a0);
  6281.         }
  6282. else copy_string(a0);
  6283.  
  6284. break;
  6285.  
  6286. @
  6287. @<Part 3@>=@[
  6288. SRTN copy_string FCN((a0))
  6289.     eight_bits a0 C1("")@;
  6290. {
  6291. app_repl(a0); /* |stringg| or |constant| */
  6292.  
  6293. while (id_first < id_loc) 
  6294.     {
  6295.     if (*id_first==@'@@') @<Simplify \.{@@@@} pairs@>@;
  6296.  
  6297.     app_repl(*id_first++);
  6298.     }
  6299.  
  6300. app_repl(a0); /* Bracket the string or constant with the id token. */
  6301. }
  6302.  
  6303. @ The following code changes doubled~\.{@@}'s to a single one.  It also
  6304. preserves any language commands, since these can appear inside vertical
  6305. bars.  Otherwise, it just deletes the character after the~`\.{@@}', thus
  6306. throwing away the entire `\.{@@}'~command.
  6307. @<Simplify \.{@@@@} pairs@>=
  6308.  
  6309. if(language == TEX && *(id_first+1) == @'@@') id_first++;
  6310. else
  6311.     {
  6312.     id_first++; // Character after the~`\.{@@}'.
  6313.  
  6314.     switch(ccode[*id_first])
  6315.         {
  6316.        case @'@@':
  6317.         break;  // The `\.{@@}'~will be stored.
  6318.  
  6319.        @<Specific language cases@>:
  6320.        case L_switch:
  6321.         app_repl(@'@@');
  6322.         break; // Retain the character.
  6323.  
  6324.        default:
  6325.         id_first++; // Discard character after~`\.{@@}'.
  6326.         continue;
  6327.         }
  6328.     }
  6329.  
  6330. @
  6331. @<Convert hex...@>=
  6332. @{
  6333. app_converted(xtoi(id_first,id_loc)); // Start after the \.{0x}.
  6334. }
  6335.  
  6336. @ A function that converts an alpha string to hex.
  6337. @<Part 3@>=@[
  6338.  
  6339. unsigned long xtoi FCN((b,b1))
  6340.     CONST ASCII HUGE *b C0("Beginning of string.")@;
  6341.     CONST ASCII HUGE *b1 C1("End of string.")@;
  6342. {
  6343. unsigned long n = 0;
  6344.  
  6345. for(b += 2; b<b1; b++)
  6346.     {
  6347.     n *= 16;
  6348.  
  6349.     if(isDigit(*b)) n += *b - @'0';
  6350.     else n += A_TO_UPPER(*b) - @'A' + 10;
  6351.     }
  6352.  
  6353. return n;
  6354. }
  6355.  
  6356. @
  6357. @<Part 3@>=@[
  6358. SRTN app_converted FCN((n))
  6359.     unsigned long n C1("")@;
  6360. {
  6361. ASCII temp[N_IDBUF];
  6362. ASCII HUGE *b;
  6363.  
  6364. SPRINTF(N_IDBUF,(outer_char *)(temp),`"%lu",n`);
  6365. to_ASCII((outer_char *)(temp));
  6366.  
  6367. app_repl(constant);
  6368.     for(b=temp; *b != '\0'; b++) app_repl(*b)@;
  6369. app_repl(constant);
  6370. }
  6371.  
  6372. @
  6373. @<Convert octal...@>=
  6374. {
  6375. app_converted(otoi(id_first,id_loc));
  6376. }
  6377.  
  6378. @ A function that converts an octal character string to integer.
  6379. @<Part 3@>=@[
  6380.  
  6381. unsigned long otoi FCN((b,b1))
  6382.     CONST ASCII HUGE *b C0("Beginning of string.")@;
  6383.     CONST ASCII HUGE *b1 C1("End of string.")@;
  6384. {
  6385. unsigned long n = 0;
  6386.  
  6387. for(b++; b<b1; b++)
  6388.     n = 8*n + *b - @'0';
  6389.  
  6390. return n;
  6391. }
  6392.  
  6393. @
  6394. @<Convert bin...@>=
  6395. {
  6396. app_converted(btoi(id_first,id_loc)); // Start after the \.{0x}.
  6397. }
  6398.  
  6399. @ A function that converts an binary character string to integer.
  6400. @<Part 3@>=@[
  6401.  
  6402. unsigned long btoi FCN((b,b1))
  6403.     CONST ASCII HUGE *b C0("Beginning of string.")@;
  6404.     CONST ASCII HUGE *b1 C1("End of string.")@;
  6405. {
  6406. unsigned long n = 0;
  6407.  
  6408. for(b+=2; b<b1; b++)
  6409.     n = 2*n + *b - @'0';
  6410.  
  6411. return n;
  6412. }
  6413.  
  6414. @ In \Ratfor-77 mode, character constants must be converted to integers. We
  6415. allow the standard ANSI escapes.
  6416. @<Part 3@>=@[
  6417. SRTN rdc_char_constant(VOID)
  6418. {
  6419. int n;
  6420.  
  6421. if(*++id_first == @'\\')
  6422.     switch(*++id_first)
  6423.         {
  6424.         @<Convert escape characters@>@;
  6425.         default:
  6426.             err_print(T,"Invalid escape sequence '\\%c' \
  6427. in Ratfor character constant; null assumed",XCHR(*id_first));
  6428.             n = 0;
  6429.             break;
  6430.         }
  6431. else n = *id_first;
  6432.  
  6433. if(*(id_first+1) != @'\'') ERR_PRINT(T,"Ratfor character constant longer \
  6434. than one byte; extra characters ignored");
  6435.  
  6436. app_converted(n);
  6437. }
  6438.  
  6439. @ Here are the standard ANSI escape sequences. The fragment isn't a
  6440. complete \&{switch} because we use it in several places, and the
  6441. \&{default} differs for each usage.
  6442.  
  6443. @<Convert escape char...@>=
  6444.         case @'0': n = '\0'; @+ break;
  6445.         case @'\\': n = @'\\'; @+ break;
  6446.         case @'\'': n = @'\''; @+ break;
  6447.         case @'"': n = @'\"'; @+ break;
  6448.         case @'?': n = @'\?'; @+ break; /* Microsoft doesn't like. */
  6449.         case @'a': n = @'\007'; @+ break; /* SGI didn't understand. */
  6450.         case @'b': n = @'\b'; @+ break;
  6451.         case @'f': n = @'\f'; @+ break;
  6452.         case @'n': n = @'\n'; @+ break;
  6453.         case @'r': n = @'\r'; @+ break;
  6454.         case @'t': n = @'\t'; @+ break;
  6455.         case @'v': n = @'\v'; @+ break;
  6456.  
  6457. @ At this point, we're positioned after the~`\.{@@}', on the starting
  6458. delimiter in a construction such as~`\.{@@'a'}', `\.{@@'\\n'}',
  6459. or~`\.{@@'\\007}'; or `\.{@@"abc"}'.
  6460.  
  6461. @<Part 3@>=
  6462. SRTN cp_ASCII(VOID)
  6463. {
  6464. if(*id_first++ == @'\'') 
  6465.     { /* Single |ASCII| character. */
  6466.     if(C_LIKE(language)) app_aconst('o',YES); // Octal (leading zero).
  6467.     else app_aconst('d',NO); // Decimal.
  6468.     }
  6469. else
  6470.     { /* Do whole string. */
  6471.     if(C_LIKE(language))
  6472.         {
  6473.         app_repl(@'"');
  6474.  
  6475.         while(*id_first != @'"')
  6476.             {
  6477.             app_repl(@'\\');
  6478.             app_aconst('o',NO); // Octal, no leading zero.
  6479.             }
  6480.  
  6481.         app_repl(@'"');
  6482.         }
  6483.     else
  6484.         {
  6485.         sixteen_bits a;
  6486.         ASCII delim = (ASCII)(is_RATFOR_(language) ? @'"' : @'\'');
  6487.         int n = STRLEN(t_style.ASCII_fcn);
  6488.  
  6489. /*  Preface by function call from style file. */
  6490.         a = ID_NUM(t_style.ASCII_fcn,t_style.ASCII_fcn+n);
  6491.         @<Append identifier token@>@;
  6492.         app_repl(@'(');
  6493.         app_repl(delim);
  6494.         while(*id_first != @'"')
  6495.             app_repl(*id_first++);
  6496.         app_repl(delim);
  6497.         app_repl(@')');
  6498.         }
  6499.     }
  6500.  
  6501. #if(0) /* Keep around for compilers that can't handle the above. */
  6502. /* Do whole string, essentially converting to form
  6503. ``\.{@@'a',@@'b',@@'c'}''. */
  6504.     app_repl(@'{');
  6505.  
  6506.     while(*id_first != @'"')
  6507.         {
  6508.         app_aconst(YES);
  6509.         app_repl(@',');
  6510.         }
  6511.  
  6512.     app_repl(@'0'); // String terminator.
  6513.     app_repl(@'}');
  6514.     }        
  6515. #endif
  6516. }
  6517.  
  6518. @ Append the translation of an |ASCII| constant.
  6519. @<Part 3@>=
  6520.  
  6521. SRTN app_aconst FCN((fmt_char,leading_zero))
  6522.     outer_char fmt_char C0("Either 'o' (octal) or 'd' (decimal)")@;
  6523.     boolean leading_zero C1("For octal format")@;
  6524. {
  6525. eight_bits n; // Value of the constant.
  6526. outer_char value[10],*v;
  6527.  
  6528. if (*id_first==@'@@') 
  6529.     { /* The construction `\.{@@'@@@@'}'. */
  6530.     n = *id_first++; // Advance past first~`\.{@@}'.
  6531.  
  6532.     if (*id_first != @'@@') ERR_PRINT(T,"Should use double @@ within \
  6533. ASCII constant");
  6534.     else id_first++;
  6535.     }
  6536. else if (*id_first==@'\\') 
  6537.     { /* Something like `\.{@@'\\040'}' or~`\.{@@'\\n'}', or it could
  6538. be an escaped delimiter. */
  6539.     id_first++; // Advance past the escape character.
  6540.  
  6541.     n = esc_achar((CONST ASCII HUGE * HUGE *)&id_first);
  6542.  
  6543. @#if 0
  6544.     switch (*id_first) 
  6545.     {
  6546.     @<Convert escape char...@>@;
  6547.     default: err_print(T,"Invalid escape sequence '\\%c' \
  6548. in ASCII constant; null assumed",XCHR(*id_first));
  6549.         n = 0;
  6550.         break;
  6551.     }
  6552. @#endif
  6553.     }
  6554. else n = *id_first++; // ``Ordinary construction'' like `\.{@@'a'}'.
  6555.  
  6556. /* The following statement is for development while debugging the character
  6557. set translations.  From a normal \FTANGLE, \.{touch \{ftangle,common\}.web}
  6558. and run \.{make} with 
  6559. ``\.{DEBUGGING=-mscramble\_ASCII}''.  This adds some extra code to scramble
  6560. all the |ASCII| constants.  Then define |DEBUG_XCHR| in
  6561. \.{custom.h} and run \.{make} with ``\.{DEBUGGING=-a}''; this scrambles the
  6562. |ASCII| constants but also compiles using the new translation table.
  6563. Hopefully, it should work as before. */
  6564. #ifdef scramble_ASCII
  6565.     n = xxord[n];
  6566. #endif
  6567.  
  6568. #ifdef unscramble_ASCII
  6569.     n = XCHR(n);
  6570. #endif
  6571.  
  6572. /* Now |n|~has the numerical value of the |ASCII| constant; in octal, it's
  6573. something like~|0123|.  We now just append the octal representation as a
  6574. constant. */
  6575.   app_repl(constant);
  6576.  
  6577.   SPRINTF(10,value,`fmt_char=='o' ? "%s%o" : "%s%d",
  6578.     leading_zero ? "0" : "",n`);
  6579.  
  6580.   for(v=value; *v; v++)
  6581.     app_repl(XORD(*v));
  6582.  
  6583.   app_repl(constant);
  6584.  
  6585. #if(0) /* Kept around in case compiler can't understand \.{\%o}. */
  6586. int l;
  6587.  
  6588. if(leading_zero) app_repl(@'0'); // Beginning zero signifies octal constant.
  6589.  
  6590.   value[0] = @'0' + (n>>6); // Left-most digit.
  6591.   value[1] = @'0' + ((n-0100*(n>>6))>>3); // Center digit.
  6592.   value[2] = @'0' + (n-010*(n>>3)); // Right-most digit.
  6593.  
  6594.   for(l=0; l<3; l++)
  6595.     if(value[l] != @'0') break; // Kill off leading zeros for beauty.
  6596.  
  6597.   for( ; l<3; l++)
  6598.     app_repl(value[l]); // Nontrivial part.
  6599.  
  6600. #endif
  6601. }
  6602.  
  6603. @ Within macros, the \.{@'\dots'} constructions only works if the quote is
  6604. explicit, not if it's returned from another \WEB\ macro.  Therefore, we
  6605. introduce a built-in, to be used as `\.{\$A('\\321')}', which expands to its
  6606. argument if |translate_ASCII| is off or to `\.{0321}' if it's on.
  6607.  
  6608. @<Define internal macros@>=
  6609.  
  6610. SAVE_MACRO("_A(s)$$ASCII(s)");
  6611. SAVE_MACRO("$A(s)$$ASCII(s)");
  6612.  
  6613. SAVE_MACRO("_ASCII(s)$$ASCII(s)");
  6614. SAVE_MACRO("$ASCII(s)$$ASCII(s)");
  6615.  
  6616. @
  6617. @<Part 3@>=@[
  6618.  
  6619. SRTN i_ascii_ FCN((n,pargs))
  6620.     int n C0("")@;
  6621.     PARGS pargs C1("")@;
  6622. {
  6623. int len; // Length to copy.
  6624. eight_bits *start = pargs[0] + 1; // Starting address of argument.
  6625.  
  6626. CHK_ARGS("$A",1);
  6627.  
  6628. if(translate_ASCII)
  6629.     {
  6630.     eight_bits HUGE *tok_ptr0 = tok_ptr; // Save starting position.
  6631.  
  6632.     if(*start == stringg) id_first = (ASCII HUGE *)(start + 1);
  6633.     else
  6634.         {
  6635.         err_print(T,"Argument of _A should be quoted; \
  6636. just returning argument");
  6637.         goto just_return;
  6638.         }
  6639.  
  6640.     cp_ASCII(); // Build result in the token area.
  6641.  
  6642.     len = tok_ptr - tok_ptr0;
  6643.     MCHECK(len,"_ascii_");
  6644.     memcpy(mp,tok_ptr0,len);
  6645.     tok_ptr = tok_ptr0; // Reset position.
  6646.     }
  6647. else
  6648.     { /* Just return the string argument. */
  6649.   just_return:
  6650.     len = pargs[1] - start;
  6651.     MCHECK(len,"_ascii_");
  6652.     STRNCPY(mp,start,len);
  6653.     }
  6654.  
  6655. mp += len;
  6656. }
  6657.   
  6658. @* SCANNING a MODULE.
  6659. The |scan_module| procedure starts when~`\.{@@\ }' or~`\.{@@*}' has been
  6660. sensed in the input, and it proceeds until the end of that module.  It
  6661. uses |module_count| to keep track of the current module number; with luck,
  6662. \.{WEAVE} and \.{TANGLE} will both assign the same numbers to modules.
  6663.  
  6664. @ The top level of |scan_module| is trivial.
  6665. @<Part 3@>=@[
  6666.  
  6667. SRTN scan_module(VOID)
  6668. {
  6669. name_pointer p = NULL; /* module name for the current module */
  6670.  
  6671. module_count++;
  6672.  
  6673. params = global_params;
  6674. frz_params();
  6675. set_output_file(global_language);
  6676.  
  6677. progress();
  6678.  
  6679. @<Scan the definition part of the current module@>; // \TeX, \.{@@d}, \.{@@f}.
  6680. @<Scan the code part of the current module@>; // Code.
  6681. }
  6682.  
  6683. @<Glob...@>=
  6684.  
  6685. EXTERN boolean is_WEB_macro SET(NO);
  6686. EXTERN boolean scanning_defn; // Deflects verbatim comments from def'n section.
  6687. EXTERN boolean scanning_TeX; /* To help out |scan_text| with the handling
  6688.                 of vertical bars. */
  6689. EXTERN boolean nuweb_mode1; // In case \.{@@N} appears in defn section.
  6690.  
  6691. EXTERN int mlevel SET(0); // Level of preprocessor expansion.
  6692.  
  6693. @ Scan either to~\.{@@a} or~\.{@@<}. The one nuance here is that for the
  6694. very first module we must absorb the predefined macros, which are sitting
  6695. in the |macro_buf|.
  6696.  
  6697. @<Scan the definition part...@>=
  6698. {
  6699. parsing_mode = INNER;
  6700. nuweb_mode1 = nuweb_mode;
  6701.  
  6702. next_control=ignore;
  6703.  
  6704. if(module_count == 1)
  6705.     {
  6706.     *(mp-1) = @'@@';
  6707.     *mp = @'m';
  6708.     divert((ASCII HUGE *)macrobuf,(ASCII HUGE *)mp,STOP); /* Begin
  6709.         reading from 
  6710.         the macro buffer, when some macros were predefined with
  6711.         |save_macro|. |mp-1| is positioned to the blank after the
  6712.         last definition. */ 
  6713.     }
  6714.  
  6715. /* Skip \TeX\ stuff and expand the definition section. */
  6716. scanning_TeX = YES;
  6717.  scan_text(macro,p,EXPAND);
  6718. scanning_TeX = NO;
  6719.  
  6720. if(module_count == 1) @<Has the |_BP| macro been defined?@>;
  6721.  
  6722. if(mlevel != 0)
  6723.     {
  6724.     err_print(M,"Invalid preprocessor block structure (level %d). \
  6725. Missing @@#endif?",mlevel);
  6726.     mlevel = 0;
  6727.     }
  6728. }
  6729.  
  6730. @ A global flag for checking preprocessor commands.
  6731.  
  6732. @<Glob...@>=
  6733.  
  6734. EXTERN boolean found_else SET(NO);
  6735.  
  6736. @ The actual work is done in this recursive function.  Preprocessor
  6737. segments are treated as independent units, processed separately with
  6738. |scan_repl|, then linked together.
  6739.  
  6740. @d MAX_LEVEL 20
  6741.  
  6742. /* We have to tell |scan_text| whether or not to expand the text it is
  6743. reading. */
  6744. @d EXPAND YES
  6745.  
  6746. @<Part 3@>=@[
  6747.  
  6748. SRTN scan_text FCN((text_type,p,expand))
  6749.     int text_type C0("Either |macro| or |module_name|.")@;
  6750.     CONST name_pointer p C0("Module name.")@;
  6751.     boolean expand C1("Do we expand?")@;
  6752. {
  6753. boolean if_switch;
  6754. boolean scanned_if = NO;
  6755. boolean first_text = YES;
  6756. eight_bits HUGE *pp;
  6757. text_pointer q;
  6758.  
  6759. scanning_defn = BOOLEAN(text_type==macro);
  6760.  
  6761. if(++mlevel >= MAX_LEVEL) 
  6762.     FATAL("!! Conditional nesting depth exceeded",""); /* Increment and
  6763. remember the incoming processing level. */  
  6764.  
  6765. WHILE()
  6766.     {
  6767.     if(scanning_defn && expand)
  6768.       {
  6769.       while(next_control<=ignore_defn)
  6770.         { // Skip \TeX\ stuff, \.{@@f}, \.{@@l}, \.{@@v}, and \.{@@W}.
  6771.          if((next_control=
  6772.             skip_ahead(next_control,YES))==module_name)  
  6773.             { /* scan the module name too */
  6774.              loc-=2; next_control=get_next();
  6775.             }
  6776.         }
  6777.       scanning_TeX = NO;
  6778.        }
  6779.     else /* Process incoming code text. */
  6780.        if(!expand) 
  6781.         {
  6782.         while( (next_control = 
  6783.               skip_ahead(next_control,YES)) == module_name)
  6784.              if( (next_control=skip_ahead(next_control,YES)) != ignore)
  6785.                 ERR_PRINT(T,"Expected @@> after @@<");
  6786.         }
  6787.        else
  6788.         { /* Process another complete fragment of code. */
  6789.         @<Insert the module number into |tok_mem|@>@;
  6790.         scan_repl(module_name,stop_the_scan); /* Now |cur_text|
  6791. points to the replacement text. */ 
  6792.         @<Update the data structure so that the replacement text is
  6793. accessible@>@; 
  6794.         first_text = NO;
  6795.         }
  6796.  
  6797.    next_macro_token:
  6798.     switch(next_control)
  6799.         {
  6800.        @<Preprocessor cases@>@;
  6801.  
  6802.        case new_output_file:
  6803.         err_print(T,"@@O and @@o are allowed only in the code \
  6804. section; command ignored");
  6805.         next_control = ignore;
  6806.         loc = limit + 1;
  6807.         break;
  6808.  
  6809.        case definition: case undefinition:
  6810.        case WEB_definition:
  6811.         if(!expand) 
  6812.             next_control = ignore;
  6813.         else 
  6814.             {
  6815.             name_pointer np;
  6816.             eight_bits last_control;
  6817.  
  6818.             if((np=app_macro(last_control=next_control))
  6819.                  == NULL) continue; 
  6820.             else if(last_control==WEB_definition)
  6821.                 np->equiv = (EQUIV)cur_text;
  6822.             }
  6823.         break;
  6824.         
  6825.  
  6826.        default:
  6827.         if(next_control <= ignore_defn)
  6828.             break;
  6829.  
  6830.         mlevel--;
  6831.         return;
  6832.         }
  6833.     }    
  6834. }
  6835.         
  6836. @ The following macro implements either |m_ifdef| or |m_ifndef|. The
  6837. argument |compares| should be `|!=|' for |m_ifdef| or `|==|' for |m_ifndef|.
  6838. @d DEF_OR_NDEF(flag) 
  6839.     found_else = NO;
  6840.     if(!expand) 
  6841.         {
  6842.         to_endif(m_ifdef); 
  6843.         goto next_macro_token;
  6844.         }
  6845.     else
  6846.         {
  6847.         text_pointer m;
  6848.         if( (next_control=get_next()) != identifier)
  6849.             {
  6850.             ERR_PRINT(T,"Expected identifier after @@#ifdef \
  6851. or @@#ifndef; assuming not defined");
  6852.             if_switch = NO;
  6853.             }
  6854.         else if_switch =
  6855.           BOOLEAN(flag((m=MAC_LOOKUP(ID_NUM(id_first,id_loc)))!=NULL
  6856.             && !(m->built_in))); 
  6857.     /* Is the identifier defined as a WEB macro? */
  6858.         if(if_switch) 
  6859.             {
  6860.             GET_LINE; /* Skip possible comment at end of
  6861. \.{@@\#ifdef} or \.{@@\#ifndef}. */
  6862.             scan_text(text_type,p,if_switch);
  6863.             }
  6864.         else 
  6865.             {
  6866.             expand=NO; @+ to_else(); 
  6867.  
  6868.             if(next_control != m_endif) 
  6869.                 {
  6870.                 scanned_if = YES;
  6871.                 goto next_macro_token;
  6872.                 }
  6873.             else 
  6874.                 {
  6875.                 next_control = ignore;
  6876.                 expand = YES;
  6877.                 GET_LINE; /* Skip possible comment after
  6878. \.{@@\#endif}. */
  6879.                 break;
  6880.                 }
  6881.             }
  6882.         }
  6883.  
  6884. /* The following were changed from |TRUE| and |FALSE| to avoid difficulties
  6885. with the VAX' \.{cc}.  */
  6886. @d M_TRUE
  6887. @d M_FALSE !
  6888.  
  6889. @<Preprocessor cases@>=
  6890.  
  6891. case m_ifdef:
  6892.     DEF_OR_NDEF(M_TRUE);
  6893.     break;
  6894.  
  6895. case m_ifndef:
  6896.     DEF_OR_NDEF(M_FALSE);
  6897.     break;
  6898.  
  6899. case m_if:
  6900.     found_else = NO;
  6901.  
  6902.     if(!expand) 
  6903.         {
  6904.         to_endif(m_if); 
  6905.         goto next_macro_token;
  6906.         }
  6907.     else 
  6908.         @<Expand an |if| statement@>@;
  6909.  
  6910.     break;
  6911.                 
  6912. case m_elif:
  6913. /* The |elif| is essentially the inverse of |if|. If we were in the midst
  6914. of an expansion, everything else must be skipped until |endif|. This is
  6915. done via |to_endif|; we must process the |endif| again in order to return
  6916. properly from the recursive scan in progress.  If we were not in the midst
  6917. of an expansion, we got here via a |to_else|; we must now proceed just as
  6918. though this were an |if|. */
  6919.     next_control = ignore;
  6920.  
  6921.     if( (mlevel==1 && !scanned_if) || found_else)
  6922.         {
  6923.         OUT_OF_ORDER("elif");
  6924.         break;
  6925.         }
  6926.     
  6927.     scanned_if = NO;
  6928.  
  6929.     if(expand) 
  6930.         {
  6931.         to_endif(m_elif); 
  6932.         goto next_macro_token;
  6933.         }
  6934.     else 
  6935.         @<Expand an |if|...@>@;
  6936.  
  6937.     expand = YES;
  6938.     break;
  6939.  
  6940. case m_else:
  6941. /* When processing an |else|, we take action based on the opposite of the
  6942. |expand| flag currently in effect. If |expand == YES|, we must then skip
  6943. everything else until the |endif|. This is done with |to_end|; we must
  6944. process the |endif| again in order to return properly from the recursion in
  6945. progress when we got here.  If |expand == NO|, we got here via a |to_else|;
  6946. we must now expand everything until the |endif|. */
  6947.     next_control = ignore;
  6948.  
  6949.     if( (mlevel == 1 && !scanned_if) || found_else)
  6950.         {
  6951.         OUT_OF_ORDER("else");
  6952.         break;
  6953.         }
  6954.  
  6955.     found_else = YES;
  6956.     scanned_if = NO;
  6957.  
  6958.     expand = BOOLEAN(!expand);
  6959.  
  6960.     GET_LINE; // Skip possible comment after \.{@@\#else}.
  6961.  
  6962.     if(expand) 
  6963.         scan_text(text_type,p,expand);
  6964.     else 
  6965.         {
  6966.         to_endif(m_else); 
  6967.         expand = YES;
  6968.         goto next_macro_token;
  6969.         }
  6970.  
  6971.     break;
  6972.  
  6973. case m_endif:
  6974.     next_control = ignore;
  6975.  
  6976.     if(mlevel == 1)
  6977.         {
  6978.         OUT_OF_ORDER("endif");
  6979.         break;
  6980.         }
  6981.  
  6982.     found_else = NO;
  6983.     GET_LINE; // Skip possible comment after \.{@@\#endif}.
  6984.     mlevel--;
  6985.     return; // Ends recursion on |scan_text|.
  6986.  
  6987. case m_undef:
  6988.     if(!expand) 
  6989.         next_control = ignore;
  6990.     else
  6991.         {
  6992.         if( (next_control=get_next()) != identifier)
  6993.             ERR_PRINT(M,"Identifier must follow @@#undef");
  6994.         else 
  6995.             {
  6996.             undef(ID_NUM(id_first,id_loc),COMPLAIN);  
  6997.             GET_LINE; /* Skip possible comment at end of
  6998. \.{@@\#undef}. */
  6999.             }
  7000.         }
  7001.     break;
  7002.  
  7003. case m_for:
  7004. case m_endfor:
  7005. case m_pragma:
  7006.     if(!expand) next_control = ignore;
  7007.     else
  7008.         {
  7009.           ERR_PRINT(M,"Sorry, preprocessor command isn't implemented yet");
  7010.         }
  7011.     break;
  7012.  
  7013. @
  7014.  
  7015. @d GET_LINE@/
  7016.      if(!from_buffer) 
  7017.         if(language!=TEX)
  7018.             get_line()@;
  7019.  
  7020. @<Expand an |if|...@>=
  7021. {
  7022. @<Evaluate conditional expression and set |if_switch|@>;
  7023. GET_LINE; // Skip possible comment at end of \.{@@\#if}.
  7024.  
  7025. if(if_switch) 
  7026.     scan_text(text_type,p,if_switch);
  7027. else 
  7028.     @<Skip to |else|, |elif|, or |endif|@>@;
  7029. }
  7030.  
  7031. @ We get here when an |if| evaluated to~0. We must skip everything until
  7032. the next |elif|, |else|, or~|endif|. If the |to_else| scan gets to an
  7033. |elif| or |else|, we  go back and evaluate that token again, thus
  7034. continuing the processing. However, if we get directly to an |endif|, this
  7035. was the case |if(0)|\dots|endif|. In this case |scan_text| wasn't called
  7036. recursively at all, so we mustn't return, but should |break| instead.
  7037.  
  7038. @<Skip to |else|...@>=
  7039. {
  7040. expand=NO; @+ to_else(); 
  7041.  
  7042. if(next_control != m_endif)
  7043.     {
  7044.     scanned_if = YES;
  7045.     goto next_macro_token;
  7046.     }
  7047. else 
  7048.     {
  7049.     next_control = ignore;
  7050.     expand = YES;
  7051.     GET_LINE; // Skip possible comment after \.{@@\#endif}
  7052.     break;
  7053.     }
  7054. }
  7055.  
  7056. @ An error message for out-of-order preprocessor commands.
  7057.  
  7058. @d OUT_OF_ORDER(cmd) out_of_order((outer_char *)cmd)
  7059.  
  7060. @<Part 3@>=@[
  7061.  
  7062. SRTN out_of_order FCN((cmd))
  7063.     CONST outer_char cmd[] C1("Name of bad preprocessor command.")@;
  7064. {
  7065. err_print(M,"Ignored out-of-order \"@@#%s\" (mlevel = %d)",cmd,mlevel);
  7066. }
  7067.  
  7068. @ We get here when we're not supposed to expand the stuff after an
  7069. \&{@@\#elif}.  We must scan without expanding to the next \&{@@\#elif},
  7070. \&{@@\#else}, or \&{@@\#endif}, taking into account the possibility of
  7071. further nested \&{@@\#if}\dots\&{@@\#endif} combinations.
  7072.  
  7073. @<Part 3@>=@[
  7074.  
  7075. SRTN to_else(VOID)
  7076. {
  7077. int elevel = 0,elifs[MAX_LEVEL],elses[MAX_LEVEL],k;
  7078.  
  7079. for(k=0; k<MAX_LEVEL; k++)
  7080.     elifs[k] = elses[k] = 0;
  7081.  
  7082. WHILE()
  7083.     switch(next_control=skip_ahead(next_control,NO))
  7084.         {
  7085.         case m_if:
  7086.         case m_ifdef:
  7087.         case m_ifndef:
  7088.             elevel++;
  7089.             break;
  7090.  
  7091.         case m_elif:
  7092.             if(elses[elevel]) 
  7093.                    ERR_PRINT(M,"Can't have @@#elif after @@#else");
  7094.             elifs[elevel]++;
  7095.             if(elevel==0) return;
  7096.             break;
  7097.  
  7098.         case m_else:
  7099.             if(elses[elevel]++) 
  7100.                 ERR_PRINT(M,"Only one @@#else allowed \
  7101. (scanning to @@else)");
  7102.             if(elevel==0) 
  7103.                 {
  7104.                 if(language==TEX && !get_line())
  7105.                     loc = limit + 1;
  7106.                 return;
  7107.                 }
  7108.             break;
  7109.  
  7110.         case m_endif:
  7111.             
  7112.             elifs[elevel] = elses[elevel] = 0;
  7113.  
  7114.             if(elevel-- == 0) 
  7115.                 {
  7116.                 found_else = NO;
  7117.                 if(language==TEX && !get_line())
  7118.                     loc = limit + 1;
  7119.                 return;
  7120.                 }
  7121.             break;
  7122.  
  7123.         case new_module:
  7124.             err_print(M,"Section ended during scan for \
  7125. \"@@#else\", \"@@#elif\", or \"@@#endif\". Inserted \"@@#endif\". \
  7126. (elevel = %d)",elevel);
  7127.             if(elevel == 0)    
  7128.                 found_else = NO;
  7129.  
  7130.             return;// The |new_module| is turned into an |m_endif|.
  7131.         }
  7132. }
  7133.  
  7134. @ The function is similar to |to_else|, but we're scanning to an \&{endif}.
  7135. @<Part 3@>=@[
  7136.  
  7137. SRTN to_endif FCN((m_case))
  7138.     int m_case C1("Case that called to_endif")@;
  7139. {
  7140. int elevel = 1,elifs[MAX_LEVEL],elses[MAX_LEVEL],k;
  7141.  
  7142. for(k=0; k<MAX_LEVEL; k++)
  7143.     elifs[k] = elses[k] = 0;
  7144.  
  7145. if(m_case==m_elif) 
  7146.     elifs[elevel] = 1;
  7147. else 
  7148.     {
  7149.     if(m_case==m_else) 
  7150.         elses[elevel] = 1;
  7151.     }
  7152.  
  7153. WHILE()
  7154.     switch(next_control=skip_ahead(next_control,NO))
  7155.         {
  7156.         case m_if:
  7157.         case m_ifdef:
  7158.         case m_ifndef:
  7159.             elevel++;
  7160.             break;
  7161.  
  7162.         case m_elif:
  7163.             if(elses[elevel]) 
  7164.                    ERR_PRINT(M,"Can't have @@#elif after @@#else");
  7165.             elifs[elevel]++;
  7166.             break;
  7167.  
  7168.         case m_else:
  7169.             if(elses[elevel]++) 
  7170.                 ERR_PRINT(M,"Only one @@#else allowed \
  7171. (scanning to @@endif)");
  7172.             break;
  7173.  
  7174.         case m_endif:
  7175.  
  7176.             elifs[elevel] = elses[elevel] = 0;
  7177.  
  7178.             if(--elevel == 0) 
  7179.                 {
  7180.                 found_else = NO;
  7181.                 if(language==TEX && !get_line())
  7182.                     loc = limit + 1;
  7183.                 return;
  7184.                 }
  7185.             break;
  7186.  
  7187.         case new_module:
  7188.             err_print(M,"Section ended during scan for \
  7189. \"endif\"; inserted \"endif\". (elevel = %d)",elevel);
  7190.             if(elevel == 0) 
  7191.                 found_else = NO;
  7192.             return;
  7193.         }
  7194. }
  7195.  
  7196. @ This fragment evaluates the argument to an \.{@@\#if} or \.{@@\#elif}.
  7197. @<Evaluate conditional...@>=
  7198. {
  7199. boolean scan0 = scanning_defn;
  7200.  
  7201. scanning_defn = YES;
  7202.     scan_repl(macro,STOP);
  7203. scanning_defn = scan0;
  7204.  
  7205. cur_text->nargs = UNDEFINED_MACRO;
  7206.  
  7207. pp = xmac_text(macrobuf,cur_text->tok_start,tok_ptr); // See \.{macs.web}.
  7208. if_switch = eval(pp,mp); // See \.{eval.web}.
  7209. }
  7210.  
  7211. @ Handle a macro definition.  \WEB\ macro definitions may have the special
  7212. forms~\.{@@m*} or~\.{@@m[\dots]}.  The asterisk indicates a recursive macro
  7213. (not implemented yet).  The \.{[\dots]} construction signifies automatic
  7214. insertion material; see the next module for more details.
  7215.  
  7216. @<Part 3@>=@[
  7217.  
  7218. name_pointer app_macro FCN((last_control))
  7219.     eight_bits last_control C1("Last token processed.")@;
  7220. {
  7221. sixteen_bits a;
  7222. name_pointer np = NULL;
  7223. boolean make_recursive = NO;
  7224. ASCII insert_type[6];
  7225. int insert_num = 0;
  7226. eight_bits temp[2]; // Holds the macro identifier.
  7227. boolean nuweb_mode0 = nuweb_mode;
  7228.  
  7229. nuweb_mode = NO; // Don't parse the beginning of macro defn's literally.
  7230.  
  7231. is_WEB_macro = BOOLEAN(last_control==WEB_definition);
  7232.  
  7233. if(is_WEB_macro || C_LIKE(language))
  7234.     {
  7235.       while ((next_control=get_next())==@'\n')
  7236.         ; // Allow definition to start on  next line.
  7237.  
  7238.     if(is_WEB_macro)
  7239.         if(next_control == MAKE_RECURSIVE) 
  7240.             {
  7241.             make_recursive = YES;
  7242.             next_control=get_next();
  7243.             }
  7244.         else if(next_control == AUTO_INSERT)
  7245.             @<Set up auto insertion@>@;
  7246.  
  7247.       if (next_control!= identifier)
  7248.         {
  7249.         err_print(M,"Definition flushed in %s; must start with \
  7250. identifier", MTRANS); 
  7251. @.Definition flushed...@>
  7252.         np = NULL;
  7253.         goto done_append;
  7254.         }
  7255.  
  7256.     a = ID_NUM_ptr(np,id_first,id_loc); // The identifier.
  7257.  
  7258. /* Process auto insertion. */
  7259.     temp[0] = LEFT(a,ID0); @+ temp[1] = RIGHT(a);
  7260.     @<Store auto insertion@>@;
  7261.  
  7262. /* Append the lhs. */
  7263.     app_repl(temp[0]);
  7264.     app_repl(temp[1]);
  7265.  
  7266.     np->macro_type = IMMEDIATE_MACRO;
  7267. /* Mark this name as a macro. |macro_type| isn't otherwise used by \Tangle. */
  7268.  
  7269.       if (*loc!=@'(') 
  7270.         {
  7271.         if(is_WEB_macro) 
  7272.             {app_repl(@' ');}
  7273.             else if(C_LIKE(language))
  7274.             { /* For outer macros, identifier must be separated
  7275. from replacement text */ 
  7276.                 app_repl(stringg); app_repl(@' '); app_repl(stringg);
  7277.             }
  7278.         }
  7279.     }
  7280.  
  7281. nuweb_mode = nuweb_mode0;
  7282. scan_repl((eight_bits)macro,(boolean)(!scanning_defn)); /* Copy stuff
  7283.             verbatim. (Also sets the language.) */ 
  7284.  
  7285. /* Finish off storing the macro. */
  7286. if(is_WEB_macro) 
  7287.     @<Argize a \.{WEB} macro@>@;
  7288. else 
  7289.     cur_text->nargs = (eight_bits)CHOICE(last_control==definition, 
  7290.         OUTER_MACRO, OUTER_UNMACRO); // Mark the outer macros.
  7291.  
  7292. cur_text->text_link = macro; // |text_link=0| characterizes a macro.
  7293.  
  7294. done_append:
  7295.     is_WEB_macro = NO;    // Reset.
  7296.     return np;
  7297. }
  7298.  
  7299. @ \WEB\ macro definitions may begin with the construction \.{@@m[*]} or
  7300. \.{@@m[pmsfbi]}, indicating that in \Ratfor\ this macro is to be output
  7301. automatically after one or more of the program units |program|, |module|,
  7302. |subroutine|, |function|, |blockdata|, and |interface|.
  7303. @<Set up auto insert...@>=
  7304. {
  7305. ASCII c;
  7306.  
  7307. while((c= *loc++)!=END_AUTO_INSERT)
  7308.     {
  7309.     if(*loc == @' ')
  7310.         {
  7311.         ERR_PRINT(M,"Found space instead of ']' after automatic \
  7312. insertion material");
  7313.         break;
  7314.         }
  7315.  
  7316.     if(loc == limit) break;
  7317.  
  7318.     if(insert_num >= 6) 
  7319.         {
  7320.         if(insert_num++ == 6)
  7321.            ERR_PRINT(M,"Can't have more than 6 types of automatic \
  7322. insertion material; remaining ignored");
  7323.         continue;
  7324.         }
  7325.  
  7326.     switch(c)
  7327.         {
  7328.        case @'*':
  7329.         STRNCPY(insert_type,"pmsfbi",insert_num=6);
  7330.         break;
  7331.  
  7332.        case @'p': case @'P':
  7333.        case @'m': case @'M':
  7334.        case @'s': case @'S':
  7335.        case @'f': case @'F':
  7336.        case @'b': case @'B':
  7337.        case @'i': case @'I':
  7338.         insert_type[insert_num++] = c;
  7339.         break;
  7340.  
  7341.        default:
  7342.         ERR_PRINT(M,"Auto insertion type must be one of \
  7343. \"ibfmps\"");
  7344.         continue;
  7345.         }
  7346.     }
  7347.  
  7348. next_control = get_next();
  7349. }
  7350.  
  7351. @ Here we save the macro identifier of automatic insertion material.  
  7352.  
  7353. @m SAVE_AUTO(type) if(insert.type.end > insert.type.start)
  7354.     err_print(M,"Overriding previous auto insertion type %s",#type);
  7355.     STRNCPY(insert.type.start,temp,2);
  7356.     insert.type.end = insert.type.start + 2
  7357.  
  7358. @<Store auto insert...@>=
  7359. {
  7360. while(insert_num-- > 0)
  7361.     switch(insert_type[insert_num])
  7362.         {
  7363.        case @'p': case @'P':
  7364.         SAVE_AUTO(program);
  7365.         break;
  7366.  
  7367.        case @'m': case @'M':
  7368.         SAVE_AUTO(module);
  7369.         break;
  7370.  
  7371.        case @'s': case @'S':
  7372.         SAVE_AUTO(subroutine);
  7373.         break;
  7374.  
  7375.        case @'f': case @'F':
  7376.         SAVE_AUTO(function);
  7377.         break;
  7378.  
  7379.        case @'b': case @'B':
  7380.         SAVE_AUTO(blockdata);
  7381.         break;
  7382.  
  7383.        case @'i': case @'I':
  7384.         SAVE_AUTO(interface);
  7385.         break;
  7386.         }
  7387. }
  7388.  
  7389. @ Put argument tokens into the token list for a WEB macro, and also strip
  7390. off newlines.
  7391. @<Argize a...@>=
  7392. {
  7393. text_ptr->tok_start = tok_ptr = argize(cur_text->tok_start,tok_ptr); /* Set
  7394.     new end by possibly stripping off newlines. */ 
  7395. cur_text->Language = (boolean)global_language; // This value shouldn't matter.
  7396. cur_text->recursive = make_recursive;
  7397. }
  7398.  
  7399. @ Similarly, in order to implement the built-in command |$DEFINE|, we need
  7400. to store a macro definition that has already been fully tokenized.
  7401. @<Part 3@>=@[
  7402.  
  7403. SRTN app_dmacro FCN((p,p1))
  7404.     CONST eight_bits HUGE *p C0("Start")@;
  7405.     CONST eight_bits HUGE *p1 C1("End.")@;
  7406. {
  7407. eight_bits a0,a1;
  7408. sixteen_bits a;
  7409. name_pointer np;
  7410. boolean make_recursive = NO;
  7411.  
  7412. if(*p == MAKE_RECURSIVE)
  7413.     {
  7414.     make_recursive = YES;
  7415.     p++;
  7416.     }
  7417.  
  7418. if(p+2 > p1)
  7419.     {
  7420.     MACRO_ERR("Invalid argument to $DEFINE",YES);
  7421.     return;
  7422.     }
  7423.  
  7424. if(TOKEN1(a0 = *p++))
  7425.     {
  7426.     MACRO_ERR("$DEFINE flushed; must start with identifier",YES);
  7427.     return;
  7428.     }
  7429.  
  7430. a = IDENTIFIER(a0,a1 = *p++);
  7431. app_repl(a0);
  7432. app_repl(a1);
  7433.  
  7434. np = name_dir + a;
  7435.  
  7436. np->macro_type = IMMEDIATE_MACRO;
  7437.  
  7438. if(*p == @'=') {p++; app_repl(@' ');} // Allow for zero-argument macro.
  7439.  
  7440. while(p < p1) {app_repl(*p++);}
  7441.  
  7442. @<Make |cur_text...@>;
  7443. @<Argize a...@>;
  7444. cur_text->text_link = macro;
  7445. np->equiv = (EQUIV)cur_text;
  7446. }
  7447.  
  7448. @ In terms of |app_dmacro|, we can also implement a built-in |$DEFINE| command.
  7449. @<Define internal macros@>=
  7450.  
  7451. SAVE_MACRO("_DEFINE(defn)$$DEFINE(defn)");
  7452. SAVE_MACRO("$DEFINE(defn)$$DEFINE(defn)");
  7453.  
  7454. SAVE_MACRO("_M(defn)$$DEFINE(defn)"); /* Shorthand for above. */
  7455. SAVE_MACRO("$M(defn)$$DEFINE(defn)"); /* Shorthand for above. */
  7456.  
  7457. @ The internal |$DEFINE| function just appends a deferred macro.
  7458. @<Part 3@>=@[
  7459.  
  7460. SRTN i_define_ FCN((n,pargs))
  7461.     int n C0("")@;
  7462.     PARGS pargs C1("")@;
  7463. {
  7464. CHK_ARGS("$M",1);
  7465.  
  7466. app_dmacro(pargs[0]+1,pargs[1]);
  7467. }
  7468.  
  7469. @ We also need an |$UNDEF| command.
  7470. @<Define internal macros@>=
  7471.  
  7472. SAVE_MACRO("_UNDEF(id)$$UNDEF(#!id)");
  7473. SAVE_MACRO("$UNDEF(id)$$UNDEF(#!id)");
  7474.  
  7475. @
  7476. @<Part 3@>=@[
  7477.  
  7478. SRTN i_undef_ FCN((n,pargs))
  7479.     int n C0("")@;
  7480.     PARGS pargs C1("")@;
  7481. {
  7482. eight_bits a0;
  7483. eight_bits HUGE *p = pargs[0]+1;
  7484.  
  7485. CHK_ARGS("$UNDEF",1);
  7486.  
  7487. if(p+2 > pargs[1])
  7488.     {
  7489.     MACRO_ERR("Invalid argument to $UNDEF",YES);
  7490.     return;
  7491.     }
  7492.  
  7493. if(TOKEN1(a0 = *p++))
  7494.     {
  7495.     MACRO_ERR("$UNDEF flushed; must start with identifier",YES);
  7496.     return;
  7497.     }
  7498.  
  7499. undef(IDENTIFIER(a0,*p),COMPLAIN);
  7500. }
  7501.  
  7502. @ We can now build in some simple arithmetic macros.
  7503. @<Define internal macros@>=
  7504.  
  7505. SAVE_MACRO("_INCR(N,...)$INCR0(#!N,$EVAL(N+$IFELSE(#0,0,1,#1)))");
  7506. SAVE_MACRO("$INCR(N,...)$INCR0(#!N,$EVAL(N+$IFELSE(#0,0,1,#1)))");
  7507.  
  7508. SAVE_MACRO("_DECR(N,...)$INCR0(#!N,$EVAL(N-$IFELSE(#0,0,1,#1)))");
  7509. SAVE_MACRO("$DECR(N,...)$INCR0(#!N,$EVAL(N-$IFELSE(#0,0,1,#1)))");
  7510.  
  7511. SAVE_MACRO("_INCR0(N,N1)$$DEFINE(#!N N1)");
  7512. SAVE_MACRO("$INCR0(N,N1)$$DEFINE(#!N N1)");
  7513.  
  7514. @<Scan the code...@>=
  7515. switch (next_control) 
  7516.     {
  7517.   case begin_code: /* \.{@@a} */
  7518.     {
  7519.     params = global_params; // The unnamed module has the global state.
  7520.     frz_params();
  7521.     set_output_file(global_language);
  7522.  
  7523.     p = name_dir; 
  7524.     @<Start column mode.@>;
  7525.     break;
  7526.     }
  7527.  
  7528.   case module_name: /* \.{@@<} */
  7529.     if(cur_module) 
  7530.         {
  7531.         p = cur_module;
  7532.         params = cur_module->mod_info->params; // Restore state.
  7533.         }
  7534.     else
  7535.         { // We get here if the module name was bad.
  7536. @#if 0
  7537.         ERR_PRINT(T,"Code placed into unnamed module");
  7538.         p = name_dir;
  7539.         params = global_params;
  7540. @#endif
  7541. /* The above wasn't a good idea.  It's better to flush the module. */
  7542.         while( (next_control=skip_ahead(ignore,NO)) != new_module);
  7543.         return;
  7544.         }
  7545.  
  7546.       @<Check that |=| or |==| follows this module name, otherwise |return|@>; 
  7547.     frz_params();
  7548.     @<Start column mode.@>;
  7549.     break;
  7550.  
  7551.   default: return;
  7552.     }
  7553.  
  7554. nuweb_mode = nuweb_mode1;
  7555.  
  7556. /* Possibly turn on nuweb mode for output. */
  7557. app_repl(begin_language);
  7558. app_repl(NUWEB_OFF | nuweb_mode);
  7559. @<Make |cur_text = text_ptr|; update |text_ptr|@>;
  7560.  
  7561. next_control = ignore;
  7562. scan_text(module_name,p,EXPAND); // Expand the code section.
  7563. column_mode = NO;
  7564.  
  7565. /* Reset nuweb mode. */
  7566. app_repl(begin_language);
  7567. app_repl(NUWEB_OFF | global_params.Nuweb_mode);
  7568. @<Make |cur_text = text_ptr|; update |text_ptr|@>;
  7569.  
  7570. @<Check that |=|...@>=
  7571. {
  7572. while ((next_control=get_next()) == @'+'); // Allow optional `\.{+=}".
  7573.  
  7574. if (next_control != @'=' && next_control != eq_eq) 
  7575.     {
  7576.     err_print(T,"Code text of %s flushed; = sign is missing", MTRANS);
  7577. @.Code text flushed...@>
  7578.  
  7579.     while ((next_control=skip_ahead(ignore,NO)) != new_module);
  7580.  
  7581.     return;
  7582.     }
  7583. }
  7584.  
  7585. @ When starting a Fortran code section, skip everything after the equals
  7586. sign so we start off fresh in the column mode.
  7587. @<Start column mode.@>=
  7588.  
  7589. if(FORTRAN_LIKE(language) && !free_form_input)
  7590.     @<Set up column mode@>@;
  7591.  
  7592. @ Prepare for \Fortran's idiotic syntax.
  7593. @<Set up col...@>=
  7594. {
  7595. loc = limit+1;
  7596. column_mode = YES;
  7597. parsing_mode = OUTER;
  7598. }
  7599.  
  7600. @<Insert the module number...@>=
  7601. {
  7602. store_two_bytes((sixteen_bits)(LINE_NUM+module_count)); 
  7603. }
  7604.  
  7605. @<Update the data...@>=
  7606. {
  7607. if(p==name_dir || p==NULL) 
  7608.     { /* Unnamed module, or bad module name */
  7609.     cur_text->module_text = (first_text && mlevel==1);
  7610.  
  7611. /* The unnamed module begins in the global language.  However, subsequent
  7612. language changes within one section---e.g., by preprocessing---should be
  7613. retained. */
  7614.     if(cur_text->module_text)
  7615.         cur_text->Language = (boolean)global_language;
  7616.  
  7617.     last_unnamed->text_link = cur_text - text_info; 
  7618.          // Link the unnamed module together.
  7619.     last_unnamed = cur_text; // Present end of the unnamed module.
  7620.     }
  7621. else if (p->equiv==(EQUIV)text_info) 
  7622.     { /* First module of this name. */
  7623.     cur_text->module_text = YES;
  7624.     p->equiv = (EQUIV)cur_text;
  7625.     }
  7626. else 
  7627.     { /* Link on the |cur_text| to the linked list. */
  7628.     LANGUAGE language0;
  7629.  
  7630.     q = (text_pointer)p->equiv; // Start of the chain.
  7631.     language0 = (LANGUAGE)q->Language; // Global language of this module.
  7632.  
  7633. /* Each replacement text (for a module name) has the same language as the
  7634. first in the chain.  Thus language switching works very efficiently;
  7635. modules inherit the language of their superior.  On the other hand,
  7636. preprocessor fragments should retain the current language, as should the
  7637. fragment following a preprocessor fragment. */
  7638.     cur_text->module_text = (first_text && mlevel==1);
  7639.  
  7640.     if(cur_text->module_text) 
  7641.         cur_text->Language = (boolean)language0;
  7642.  
  7643. /* Find end of list, delimited by |module_flag|.  (There's nothing
  7644. comparable to |last_unnamed| to tell us where the end is.) */
  7645.     while (q->text_link < module_flag) q = q->text_link + text_info; 
  7646.  
  7647.     q->text_link = cur_text - text_info;
  7648.         // Append more stuff to this module by linking in |cur_text|.
  7649.     }
  7650.  
  7651. /* |cur_text| has now been linked to the end of the appropriate chain.  Use
  7652. |module_flag| as a special |text_link| to signify the end of the list. */
  7653. cur_text->text_link = module_flag;
  7654. }
  7655.  
  7656. @ In phase~1, we skip the limbo section, set the global language, then
  7657. process each module in turn. 
  7658. @<Part 3@>=@[
  7659.  
  7660. SRTN phase1(VOID) 
  7661. {
  7662. LANGUAGE language0=language;
  7663.  
  7664. phase = 1;
  7665. module_count = 0;
  7666. rst_input(); rst_out(NOT_CONTINUATION);
  7667. reading(web_file_name,NO);
  7668.  
  7669. while ((next_control=skip_ahead(ignore,NO))!=new_module)
  7670.     ; // Skip stuff before first module.  This may reset the language.
  7671.  
  7672. chk_override(language0);
  7673. fin_language(); /* Make sure flags are initialized properly. */
  7674. global_params = params; /* Remember the global parameters. */
  7675. set_output_file(global_language); /* Language in force at the
  7676.                 beginning of each module. */ 
  7677.  
  7678. while (!input_has_ended) 
  7679.     scan_module(); // Do each module one at a time.
  7680.  
  7681. chk_complete(); // Anything left in change file?
  7682. @<Count the distinct modules@>@;
  7683. }
  7684.  
  7685. @ Here we set a global variable to the number of distinct modules. This is
  7686. used later in the expansion of the built-in macro |$MODULES|. The total
  7687. number of sections is also remembered, for use in the built-in |$SECTIONS|.
  7688. @<Glob...@>=
  7689.  
  7690. EXTERN sixteen_bits num_distinct_modules SET(1); // Count the unnamed module.
  7691. EXTERN sixteen_bits num_modules;
  7692.  
  7693. @
  7694. @<Count the distinct...@>=
  7695. @{
  7696. name_pointer np;
  7697.  
  7698. @b
  7699. for(np=name_dir; np<name_ptr; np++)
  7700.        if(np->equiv != NULL && np->equiv != (EQUIV)text_info
  7701.         && np->macro_type==NOT_DEFINED)
  7702.             num_distinct_modules++; 
  7703.  
  7704. num_modules = module_count;
  7705. }
  7706.  
  7707. @ Here we  define a built-in macro that expands into the number of distinct
  7708. modules. 
  7709. @<Define internal...@>=
  7710.  
  7711. SAVE_MACRO("_MODULES $$MODULES(0)");
  7712. SAVE_MACRO("$MODULES $$MODULES(0)");
  7713.  
  7714. SAVE_MACRO("_SECTIONS $$MODULES(1)");
  7715. SAVE_MACRO("$SECTIONS $$MODULES(1)");
  7716.  
  7717. @
  7718. @<Part 3@>=@[
  7719.  
  7720. SRTN i_modules_ FCN((n,pargs))
  7721.     int n C0("")@;
  7722.     PARGS pargs C1("")@;
  7723. {
  7724. outer_char temp[50];
  7725. int m=NSPRINTF(temp,"%c%u%c",XCHR(constant),
  7726.     *(pargs[0]+2) == '0' ? num_distinct_modules : num_modules,
  7727.     XCHR(constant));
  7728.  
  7729. CHK_ARGS("$MODULES",1);
  7730.  
  7731. MCHECK(m,"_modules_");
  7732. STRCPY(mp,to_ASCII(temp));
  7733. mp += m;
  7734. }
  7735.  
  7736. @ Print statistics at end of \FTANGLE's run.
  7737. @<Part 3@>=
  7738.  
  7739. SRTN see_tstatistics(VOID)
  7740. {
  7741. CLR_PRINTF(info,("\n\nMEMORY USAGE STATISTICS:\n"));
  7742. STAT0("names",sizeof(*name_ptr),
  7743.     SUB_PTRS(name_ptr,name_dir),max_names,UPPER(max_names),",");
  7744.  
  7745. STAT0("replacement texts",sizeof(*text_ptr),
  7746.     SUB_PTRS(text_ptr,text_info),max_texts,UPPER(max_texts),",");
  7747.  
  7748. STAT0("deferred texts",sizeof(*txt_dptr),
  7749.     SUB_PTRS(txt_dptr,txt_dinfo),dtexts_max,UPPER(dtexts_max),";");
  7750.  
  7751. STAT0("bytes",sizeof(*byte_ptr),
  7752.     SUB_PTRS(byte_ptr,byte_mem),max_bytes,UPPER(max_bytes),",");
  7753.  
  7754. STAT0("tokens",sizeof(*tok_ptr),
  7755.     SUB_PTRS((mx_tok_ptr > tok_ptr ? mx_tok_ptr : tok_ptr),tok_mem),
  7756.         max_toks,UPPER(max_toks_t),",");
  7757.  
  7758. STAT0("deferred tokens",sizeof(*tok_dptr),
  7759.     SUB_PTRS((mx_dtok_ptr > tok_dptr ? mx_dtok_ptr : tok_dptr),tok_dmem),
  7760.         max_dtoks,UPPER(max_dtoks),".");
  7761.  
  7762. mem_avail(1); /* How much memory left at end of run. */
  7763. }
  7764.  
  7765. @ This is an interface to |predefine_macros| in \.{macs.web}.
  7766. @<Part 3@>=@[
  7767.  
  7768. SRTN t_macros(VOID)
  7769. {
  7770. @<Define internal...@>;
  7771. }
  7772.  
  7773. @ Send a commented message to the output file.
  7774.  
  7775. In some cases, the message we want to send might involve fragments of
  7776. code that have to be translated. Therefore, we first use |str_to_mb| to
  7777. detokenize the message, then we ship it out in the form of a meta-comment.
  7778.  
  7779. @d SPCS_AFTER_CMNT 1 // For beautification of the Ratfor error messages.
  7780.  
  7781. @<Glob...@>=
  7782.  
  7783. #if SMALL_MEMORY
  7784.     #define MSG_BUF_SIZE 5000
  7785. #else
  7786.     #define MSG_BUF_SIZE 50000L
  7787. #endif
  7788.  
  7789. @
  7790. @<Part 3@>=@[
  7791.  
  7792. SRTN out_msg FCN((msg,msg1))
  7793.     CONST ASCII *msg C0("Start of message.")@;
  7794.     CONST ASCII *msg1 C1("See the description below.")@;
  7795. {
  7796. eight_bits HUGE *temp;
  7797. eight_bits HUGE *mp0 = mp,
  7798.     HUGE *macrobuf0 = macrobuf, HUGE *macrobuf_end0 = macrobuf_end;
  7799. char HUGE *new_msg; // The translated message.
  7800. boolean nuweb_mode0,in_string0,meta_mode0;
  7801.  
  7802. /* Translate the message, which may contain identifiers, into the |macrobuf|.*/
  7803. mp = macrobuf = temp = GET_MEM("out_msg:temp",MSG_BUF_SIZE,eight_bits);
  7804. macrobuf_end = temp + MSG_BUF_SIZE;
  7805.  
  7806. /* If |msg1 != NULL|, then it denotes the end of the array. If it is
  7807. |NULL|, we assume it's an ordinary character string and determine the end. */
  7808. if(msg1==NULL) 
  7809.     msg1 = msg + STRLEN(msg);
  7810.  
  7811. new_msg = (char HUGE *)str_to_mb((eight_bits HUGE *)msg,
  7812.                 (eight_bits HUGE *)msg1,NO);
  7813.  
  7814. /* Ship it out in the form of a meta-comment. */
  7815. spcs_after_cmnt = SPCS_AFTER_CMNT;
  7816.  
  7817. /* We bracket the output message by a standard set of |nuweb_mode|,
  7818. |meta_mode|, and |in_string| in order that the top, bottom, and prefix
  7819. fields work correctly. */
  7820. nuweb_mode0 = nuweb_mode;
  7821. in_string0 = in_string;
  7822. meta_mode0 = meta_mode;
  7823.  
  7824. meta_mode = nuweb_mode = NO; 
  7825. @%in_string = YES;
  7826.  
  7827. OUT_CHAR(begin_meta); 
  7828. OUT_CHAR(begin_meta); // Second one turns off |xpn_Ratfor|.
  7829.       while(*new_msg) 
  7830.         OUT_CHAR(*new_msg++);
  7831. OUT_CHAR(end_meta);
  7832.  
  7833. nuweb_mode = nuweb_mode0;
  7834. in_string = in_string0;
  7835. meta_mode = meta_mode0;
  7836.  
  7837. spcs_after_cmnt = 0;
  7838.  
  7839. FREE_MEM(temp,"out_msg:temp",MSG_BUF_SIZE,eight_bits);
  7840. macrobuf = macrobuf0; @+ mp = mp0; @+ macrobuf_end = macrobuf_end0;
  7841. }
  7842.  
  7843. @
  7844. @<Part 3@>=
  7845.  
  7846. static sixteen_bits id_unroll;
  7847.  
  7848. SPEC univ_tokens[] = {
  7849.     {"_UNROLL",0,x_unroll,&id_unroll},
  7850.     {"$UNROLL",0,x_unroll,&id_unroll},
  7851.     {"",0,NULL,NULL}
  7852.     };
  7853.  
  7854. SRTN ini_univ_tokens FCN((language0))
  7855.     LANGUAGE language0 C1("")@;
  7856. {
  7857. ini_special_tokens(language0,univ_tokens);
  7858. }
  7859.  
  7860. @
  7861. @<Define internal macros@>=
  7862.  
  7863. SAVE_MACRO("_DO(k,kmin,kmax,...)$UNROLL(k,kmin,kmax,$IFCASE(#0,1,#.))");
  7864. SAVE_MACRO("$DO(k,kmin,kmax,...)$UNROLL(k,kmin,kmax,$IFCASE(#0,1,#.))");
  7865.  
  7866. @<Part 3@>=@[
  7867.  
  7868. SRTN ini_tokens FCN((language0))
  7869.     LANGUAGE language0 C1("")@;
  7870. {
  7871. switch(language0)
  7872.     {
  7873.    case C:
  7874.     break;
  7875.  
  7876.    case C_PLUS_PLUS:
  7877.     break;
  7878.  
  7879.    case FORTRAN:
  7880.     break;
  7881.  
  7882.    case FORTRAN_90:
  7883.     break;
  7884.  
  7885.    case TEX:
  7886.     break;
  7887.  
  7888.    default:
  7889.     break;
  7890.     }
  7891.  
  7892. ini_univ_tokens(language0);
  7893. }
  7894.  
  7895. @ Get the numerical value of a WEB |constant| string.
  7896. @<Part 3@>=@[
  7897.  
  7898. int get_constant FCN((e))
  7899.     eight_bits HUGE *e C1("")@;
  7900. {
  7901. boolean positive = YES;
  7902. int i = 1; // To prevent the increment from being~0 when an error occurs.
  7903.  
  7904. if(*e == @'-')
  7905.     {
  7906.     positive = NO;
  7907.     e++;
  7908.     }
  7909.  
  7910. if(*e++ != constant) 
  7911.     {
  7912.     ERR_PRINT(T,"Invalid loop constant");
  7913.     return i;
  7914.     }
  7915.  
  7916. to_outer(e);
  7917. i = ATOI(e);
  7918. return (positive) ? i : -i;
  7919. }
  7920.  
  7921.  
  7922.  
  7923. @* STYLE FILE. The style file is common to \FWEAVE\ and \FTANGLE. See
  7924. \.{style.web}. 
  7925.  
  7926. @<Include...@>=
  7927.  
  7928. #include "map.h" // Relations between style file keywords and internal arrays.
  7929.  
  7930. @* INDEX.  Here is a cross-reference table for the \.{TANGLE} processor.
  7931. All modules in which an identifier is used are listed with that identifier,
  7932. except that reserved words are indexed only when they appear in format
  7933. definitions, and the appearances of identifiers in module names are not
  7934. indexed. Underlined entries correspond to where the identifier was
  7935. declared. Error messages and a few other things like ``ASCII code'' are
  7936. indexed here too.
  7937.